Source code of Windows XP (NT5)
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

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. connect.cxx
  5. Abstract:
  6. Contains the entry points for the Winnet Connection API supported by the
  7. Multi-Provider Router.
  8. Contains:
  9. WNetAddConnectionW
  10. WNetAddConnection2W
  11. WNetAddConnection3W
  12. WNetUseConnectionW
  13. WNetCancelConnection2W
  14. WNetCancelConnectionW
  15. WNetGetConnectionW
  16. WNetSetConnectionW
  17. WNetRestoreConnectionW
  18. WNetRestoreConnection2W
  19. DoRestoreConnection
  20. MprRestoreThisConnection
  21. MprCreateConnectionArray
  22. MprSortConnectionArray
  23. MprRefcountConnectionArray
  24. MprAddPrintersToConnArray
  25. MprForgetPrintConnection
  26. MprFreeConnectionArray
  27. MprNotifyErrors
  28. MprNotifyShell
  29. MprCancelConnection2
  30. Author:
  31. Dan Lafferty (danl) 09-Oct-1991
  32. Environment:
  33. User Mode -Win32
  34. Notes:
  35. Revision History:
  36. 09-Oct-1991 danl
  37. created
  38. 03-Jan-1992 terryk
  39. Changed WNetRestoreConnections WNetRestoreConnection
  40. 13-Jan-1992 Johnl
  41. Added MPR.H to include file list
  42. 31-Jan-1992 Johnl
  43. Added fForgetConnection to WNetCancelConnectionW
  44. 01-Apr-1992 Johnl
  45. Changed CONNECTION_REMEMBER to CONNECT_UPDATE_PROFILE, updated
  46. WNetCancelConnection2 to match spec
  47. 22-Jul-1992 danl
  48. WNetAddConnection2: If attempting to connect to a drive that is
  49. already remembered in the registry, we will allow the connect as
  50. long as the remote name for the connection is the same as the
  51. one that is remembered. If the remote names are not the same
  52. return ERROR_DEVICE_ALREADY_REMEMBERED.
  53. 26-Aug-1992 danl
  54. WNetAddConnectionW: Put Try & except around STRLEN(lpLocalName).
  55. 04-Sept-1992 danl
  56. Re-Write MprRestoreThisConnection.
  57. 08-Sept-1992 danl
  58. WNetCancelConnect2W: If no providers claim responsibility for
  59. the cancel, and if the connect info is in the registry, then
  60. remove it, and return SUCCESS.
  61. 09-Sept-1992 danl
  62. WNetCancelConnect2W: If the provider returns WN_BAD_LOCALNAME,
  63. WN_NO_NETWORK, or WN_BAD_NETNAME, then return WN_NOT_CONNECTED.
  64. 22-Sept-1992 danl
  65. WNetRestoreConnection: For WN_CANCEL case, set continue Flag to false.
  66. We don't want to ask for password again if they already said CANCEL.
  67. 02-Nov-1992 danl
  68. Fail with NO_NETWORK if there are no providers.
  69. 24-Nov-1992 Yi-HsinS
  70. Added checking in the registry to see whether we need to
  71. restore connection or not. ( support of RAS )
  72. 16-Nov-1993 Danl
  73. AddConnect2: If provider returns ERROR_INVALID_LEVEL or
  74. ERROR_INVALID_PARAMETER, continue trying other providers.
  75. 19-Apr-1994 Danl
  76. DoRestoreConnection: Fix timeout logic where we would ignore the
  77. provider-supplied timeout if the timeout was smaller than the default.
  78. Now, if all the providers know their timeouts, the larger of those
  79. timeouts is used. Even if smaller than the default.
  80. AddConnection3: Fixed Prototype to be more like AddConnection2.
  81. 19-May-1994 Danl
  82. AddConnection3: Changed comments for dwType probe to match the code.
  83. Also re-arrange the code here to make it more efficient.
  84. 07-Feb-1995 AnirudhS
  85. MprGetConnection: Fixed so that, like AddConnection and
  86. CancelConnection, it doesn't stop routing on errors.
  87. 10-Feb-1995 AnirudhS
  88. WNetAddConnection3: If provider returns WN_ALREADY_CONNECTED, stop
  89. trying other providers.
  90. 08-May-1995 AnirudhS
  91. Add WNetUseConnection and make WNetAddConnection3 call through to it.
  92. 12-Jun-1995 AnirudhS
  93. Send WM_DEVICECHANGE message to notify shell of connections.
  94. 13-Jun-1995 AnirudhS
  95. Add WNetSetConnection.
  96. 07-Jul-1995 AnirudhS
  97. Tidy up auto-picking logic in WNetUseConnection and CAutoPickedDevice.
  98. 12-Jul-1995 AnirudhS
  99. Rename WNetRestoreConnection to WNetRestoreConnectionW to match
  100. winnetwk.w.
  101. 14-Sep-1995 AnirudhS
  102. WNetGetConnection: Optimize for non-network drives by not calling
  103. the providers. This enables the shell to open the "My Computer"
  104. folder much quicker.
  105. 18-Oct-1995 AnirudhS
  106. WNetUseConnection: When saving the connection to the registry, if a
  107. username is not supplied, get it from the provider, so that the
  108. right username will be used at the next logon.
  109. 15-Jan-1996 AnirudhS
  110. WNetRestoreConnection, etc.: Restore the connections in a deferred
  111. state if the remembered user name is the default user.
  112. 08-Mar-1996 AnirudhS
  113. WNetRestoreConnection, etc.: Use the provider type in preference to
  114. the provider name when saving and restoring connections, so that
  115. floating profiles work in a multi-language environment. See
  116. comments at MprReadConnectionInfo for details.
  117. 20-Mar-1996 Anirudhs
  118. Add WNetGetConnection3.
  119. 25-Mar-1996 AnirudhS
  120. WNetRestoreConnection, etc.: Don't display the "restoring connections"
  121. dialog if all connections are deferred. Don't defer connections if
  122. a registry flag says not to.
  123. 11-Apr-1996 AnirudhS
  124. WNetUseConnection: Convert to CRoutedOperation base class.
  125. Return WN_NO_MORE_DEVICES instead of WN_NO_MORE_ENTRIES.
  126. 04-Jun-1996 AnirudhS
  127. WNetRestoreConnection: Don't defer connections if logged on locally.
  128. This is a temporary fix for bug 36827 for NT 4.0.
  129. 07-Jun-1996 AnirudhS
  130. MprNotifyShell: Don't call BroadcastSystemMessage here. Instead use
  131. a simple scheme to have a trusted system component broadcast on our
  132. behalf. This fixes a deadlock that caused 20-second delays that were
  133. visible to the user.
  134. 28-Jun-1996 AnirudhS
  135. Don't defer a connection if a password was explicitly supplied when
  136. the connection was made.
  137. 08-Feb-1997 AnirudhS
  138. QFE 55011: Add WN_CONNECTED_OTHER_PASSWORD so the MPR knows if the
  139. provider prompted the user for an explicit password.
  140. 21-Feb-1997 AnirudhS
  141. Add DEFER_UNKNOWN so MPR automatically discovers whether to defer a
  142. connection at future logons.
  143. 03-Mar-1998 jschwart
  144. Fix bugs caused by moving WNetRestoreConnection from userinit.exe
  145. into winlogon.exe: Add impersonation code to WNetRestoreConnection
  146. so a reconnection is attempted in the user's context. Add code to
  147. close registry key handles that were leaked inWNetRestoreConnection,
  148. MprRestoreThisConnection, and CreateConnectionArray.
  149. 05-May-1999 jschwart
  150. Make provider addition/removal dynamic
  151. 04-Mar-2000 dsheldon
  152. Added WNetRestoreConnection2W with a flags field that allows us to
  153. reduce the amount of user interaction required to reconnect drives,
  154. and allow the shell to handle errors after logon instead.
  155. --*/
  156. //
  157. // INCLUDES
  158. //
  159. #include "precomp.hxx"
  160. extern "C" {
  161. #include <ntlsa.h> // LsaGetUserName
  162. #include <winsvcp.h> // SC_BSM_EVENT_NAME
  163. }
  164. #include <wincred.h>
  165. #include <shellapi.h>
  166. #include <shlapip.h>
  167. #include <tstr.h> // WCSSIZE, STRLEN
  168. #include "connify.h" // MprAddConnectNotify
  169. #include "mprui.h" // ShowReconnectDialog, etc.
  170. #include <apperr.h>
  171. //
  172. // DATA STRUCTURES
  173. //
  174. typedef struct _CONNECTION_INFO
  175. {
  176. BOOL ContinueFlag;
  177. DWORD ProviderIndex;
  178. DWORD ProviderWait;
  179. DWORD ProviderFlags;
  180. DWORD DeferFlags; // flags read from registry
  181. DWORD Status;
  182. LPTSTR UserName;
  183. BOOL Defer; // our computed decision on whether to defer
  184. NETRESOURCEW NetResource;
  185. HKEY RegKey; // handle to the key with the connection info
  186. HINSTANCE hProviderDll; // Variables to keep provider DLLs loaded
  187. PF_NPGetCaps pfGetCaps;
  188. PF_NPAddConnection3 pfAddConnection3;
  189. PF_NPAddConnection pfAddConnection;
  190. PF_NPCancelConnection pfCancelConnection;
  191. DWORD dwConnectCaps;
  192. }
  193. CONNECTION_INFO, *LPCONNECTION_INFO;
  194. //
  195. // EXTERNAL GLOBALS
  196. //
  197. extern DWORD GlobalNumActiveProviders;
  198. extern BOOL g_LUIDDeviceMapsEnabled;
  199. //
  200. // Defines
  201. //
  202. #define INVALID_WINDOW_HANDLE ((HWND) INVALID_HANDLE_VALUE)
  203. //
  204. // Local Function Prototypes
  205. //
  206. VOID
  207. DoRestoreConnection(
  208. PARAMETERS * Params
  209. );
  210. DWORD
  211. MprRestoreThisConnection(
  212. HWND hWnd,
  213. PARAMETERS * Params,
  214. LPCONNECTION_INFO ConnectInfo,
  215. DWORD dwFlags
  216. );
  217. DWORD
  218. MprCreateConnectionArray(
  219. LPDWORD lpNumConnections,
  220. LPCTSTR lpDevice,
  221. LPDWORD lpRegMaxWait,
  222. LPCONNECTION_INFO *ConnectArray
  223. );
  224. VOID
  225. MprSortConnectionArray(
  226. LPCONNECTION_INFO lpcConnectArray,
  227. DWORD dwNumSubKeys
  228. );
  229. VOID
  230. MprRefcountConnectionArray(
  231. LPCONNECTION_INFO lpcConnectArray,
  232. DWORD dwNumSubkeys
  233. );
  234. DWORD
  235. MprAddPrintersToConnArray(
  236. LPDWORD lpNumConnections,
  237. LPCONNECTION_INFO *ConnectArray
  238. );
  239. VOID
  240. MprFreeConnectionArray(
  241. LPCONNECTION_INFO ConnectArray,
  242. DWORD NumConnections
  243. );
  244. BOOL
  245. MprUserNameMatch (
  246. IN PUNICODE_STRING DomainName,
  247. IN PUNICODE_STRING UserName,
  248. IN LPCWSTR RememberedName,
  249. IN BOOL fMustMatchCompletely
  250. );
  251. DWORD
  252. MprNotifyErrors(
  253. HWND hWnd,
  254. LPCONNECTION_INFO ConnectArray,
  255. DWORD NumConnections,
  256. DWORD dwFlags
  257. );
  258. VOID
  259. MprNotifyShell(
  260. IN LPCWSTR pwszDevice
  261. );
  262. DWORD
  263. MprCancelConnection2 (
  264. IN LPCWSTR lpName,
  265. IN DWORD dwFlags,
  266. IN BOOL fForce,
  267. IN BOOL fCheckProviders
  268. );
  269. BOOL
  270. MprBroadcastDriveChange(
  271. IN LPCWSTR pwszDevice,
  272. IN BOOL DeleteAction
  273. );
  274. DWORD
  275. WNetAddConnectionW (
  276. IN LPCWSTR lpRemoteName,
  277. IN LPCWSTR lpPassword,
  278. IN LPCWSTR lpLocalName
  279. )
  280. /*++
  281. Routine Description:
  282. This function allows the caller to redirect (connect) a local device
  283. to a network resource. The connection is remembered.
  284. Arguments:
  285. lpRemoteName - Specifies the network resource to connect to.
  286. lpPassword - Specifies the password to be used in making the connection.
  287. The NULL value may be passed in to indicate use of the 'default'
  288. password. An empty string may be used to indicate no password.
  289. lpLocalName - This should contain the name of a local device to be
  290. redirected, such as "F:" or "LPT1:" The string is treated in a
  291. case insensitive manner, and may be the empty string in which case
  292. a connection to the network resource is made without making a
  293. decision.
  294. Return Value:
  295. --*/
  296. {
  297. DWORD status = WN_SUCCESS;
  298. NETRESOURCEW netResource;
  299. DWORD numchars;
  300. //
  301. // load up the net resource structure
  302. //
  303. netResource.dwScope = 0;
  304. netResource.dwUsage = 0;
  305. netResource.lpRemoteName = (LPWSTR) lpRemoteName;
  306. netResource.lpLocalName = (LPWSTR) lpLocalName;
  307. netResource.lpProvider = NULL;
  308. netResource.lpComment = NULL;
  309. __try {
  310. numchars = STRLEN(lpLocalName);
  311. }
  312. __except(EXCEPTION_EXECUTE_HANDLER) {
  313. status = GetExceptionCode();
  314. if (status != EXCEPTION_ACCESS_VIOLATION) {
  315. MPR_LOG(ERROR,"WNetAddConnectionW:Unexpected Exception 0x%lx\n",status);
  316. }
  317. status = WN_BAD_POINTER;
  318. }
  319. if (status != WN_SUCCESS) {
  320. SetLastError(status);
  321. return status;
  322. }
  323. if (numchars == 0) {
  324. netResource.dwType = RESOURCETYPE_ANY;
  325. }
  326. else if (numchars > 2) {
  327. netResource.dwType = RESOURCETYPE_PRINT;
  328. }
  329. else {
  330. netResource.dwType = RESOURCETYPE_DISK;
  331. }
  332. //
  333. // Call WNetUseConnection so it can do all the work.
  334. //
  335. return(WNetUseConnectionW (
  336. NULL, // hwndOwner
  337. &netResource, // lpNetResource
  338. lpPassword, // lpPassword
  339. NULL, // lpUserID
  340. CONNECT_UPDATE_PROFILE, // dwFlags
  341. NULL, // lpAccessName
  342. NULL, // lpBufferSize
  343. NULL)); // lpResult
  344. }
  345. DWORD
  346. WNetAddConnection2W (
  347. IN LPNETRESOURCEW lpNetResource,
  348. IN LPCWSTR lpPassword,
  349. IN LPCWSTR lpUserName,
  350. IN DWORD dwFlags
  351. )
  352. /*++
  353. Routine Description:
  354. This function allows the caller to redirect (connect) a local device
  355. to a network resource. It is similar to WNetAddConnection, except
  356. that it takes a pointer to a NETRESOURCE structure to describe the
  357. network resource to connect to. It also takes the additional parameters
  358. lpUserName, and dwFlags.
  359. Arguments:
  360. lpNetResource - This is a pointer to a network resource structure that
  361. specifies the network resource to connect to. The following
  362. fields must be set when making a connection, the others are ignored.
  363. lpRemoteName
  364. lpLocalName
  365. lpProvider
  366. dwType
  367. lpPassword - Specifies the password to be used in making the connection.
  368. The NULL value may be passed in to indicate use of the 'default'
  369. password. An empty string may be used to indicate no password.
  370. lpUserName- This specifies the username used to make the connection.
  371. If NULL, the default username (currently logged on user) will be
  372. applied. This is used when the user wishes to connect to a
  373. resource, but has a different user name or account assigned to him
  374. for that resource.
  375. dwFlags - This is a bitmask which may have any of the following bits set:
  376. CONNECT_UPDATE_PROFILE
  377. Return Value:
  378. --*/
  379. {
  380. //
  381. // Call WNetUseConnection so it can do all the work.
  382. // It is called with a NULL HWND.
  383. //
  384. return(WNetUseConnectionW (
  385. NULL, // hwndOwner
  386. lpNetResource, // lpNetResource
  387. lpPassword, // lpPassword
  388. lpUserName, // lpUserID
  389. dwFlags, // dwFlags
  390. NULL, // lpAccessName
  391. NULL, // lpBufferSize
  392. NULL)); // lpResult
  393. }
  394. DWORD
  395. WNetAddConnection3W (
  396. IN HWND hwndOwner,
  397. IN LPNETRESOURCEW lpNetResource,
  398. IN LPCWSTR lpPassword,
  399. IN LPCWSTR lpUserName,
  400. IN DWORD dwFlags
  401. )
  402. /*++
  403. Routine Description:
  404. This function allows the caller to redirect (connect) a local device
  405. to a network resource. It is similar to WNetAddConnection2, except
  406. that it takes the additional parameter hwndOwner.
  407. Arguments:
  408. hwndOwner - A handle to a window which should be the owner for any
  409. messages or dialogs that the network provider might display.
  410. lpNetResource - This is a pointer to a network resource structure that
  411. specifies the network resource to connect to. The following
  412. fields must be set when making a connection, the others are ignored.
  413. lpRemoteName
  414. lpLocalName
  415. lpProvider
  416. dwType
  417. lpPassword - Specifies the password to be used in making the connection.
  418. The NULL value may be passed in to indicate use of the 'default'
  419. password. An empty string may be used to indicate no password.
  420. lpUserName- This specifies the username used to make the connection.
  421. If NULL, the default username (currently logged on user) will be
  422. applied. This is used when the user wishes to connect to a
  423. resource, but has a different user name or account assigned to him
  424. for that resource.
  425. dwFlags - This is a bitmask which may have any of the following bits set:
  426. CONNECT_UPDATE_PROFILE
  427. Return Value:
  428. --*/
  429. {
  430. //
  431. // Call WNetUseConnection so it can do all the work.
  432. // It is called with no buffer for the access name and result flags.
  433. //
  434. return(WNetUseConnectionW (
  435. hwndOwner, // hwndOwner
  436. lpNetResource, // lpNetResource
  437. lpPassword, // lpPassword
  438. lpUserName, // lpUserID
  439. dwFlags, // dwFlags
  440. NULL, // lpAccessName
  441. NULL, // lpBufferSize
  442. NULL)); // lpResult
  443. }
  444. /*************************************************************************
  445. NAME: CAutoPickedDevice
  446. SYNOPSIS: This class is meant for use by the WNetUseConnection
  447. function. It iterates through all possible local device names
  448. and finds an unused device name to be redirected. For efficiency,
  449. it does this only once in the lifetime of the object, viz. the
  450. first time the PickDevice() method is called. On subsequent calls
  451. it returns the result of the first call.
  452. The results of the PickDevice() call are saved in member variables,
  453. rather than being copied to the caller's buffer, because we don't
  454. know whether to return them to the caller until later.
  455. INTERFACE:
  456. CAVEATS: The Init method must be called before the PickDevice method.
  457. NOTES:
  458. HISTORY:
  459. AnirudhS 24-May-1995 Created
  460. **************************************************************************/
  461. class CAutoPickedDevice
  462. {
  463. private:
  464. DWORD _bPicked; // whether already attempted to pick a device
  465. DWORD _dwError; // result of attempt to pick a device
  466. DWORD _dwDeviceType; // saved parameter from Init()
  467. DWORD _cchBufferSize; // saved parameter from Init()
  468. DWORD _cchReqBufferSize; // saved results of PickDevice()
  469. WCHAR _wszPickedName[ max(sizeof("A:"), sizeof("LPT99:")) ];
  470. public:
  471. DWORD Init(
  472. IN LPWSTR lpAccessName,
  473. IN LPDWORD lpcchBufferSize,
  474. IN LPCWSTR pwszLocalName,
  475. IN LPCWSTR pwszRemoteName,
  476. IN DWORD dwDeviceType,
  477. IN DWORD dwFlags
  478. );
  479. DWORD PickDevice();
  480. DWORD dwError()
  481. { return _dwError; }
  482. DWORD cchReqBufferSize()
  483. { return _cchReqBufferSize; }
  484. LPWSTR wszPickedName()
  485. { return _wszPickedName; }
  486. } ;
  487. /*************************************************************************
  488. NAME: CAutoPickedDevice::Init
  489. SYNOPSIS: This function validates the parameters related to access
  490. names and auto-picking of local device names, and saves away some
  491. of them to be used in PickDevice().
  492. Depending on the severity of the errors it finds, it does one of
  493. the following:
  494. - cause an access violation
  495. - return an error
  496. - return success, but save away an error to be returned from
  497. PickDevice()
  498. - return success, and save away WN_SUCCESS so that PickDevice()
  499. will try to pick a device
  500. **************************************************************************/
  501. DWORD CAutoPickedDevice::Init(
  502. IN LPWSTR lpAccessName,
  503. IN LPDWORD lpcchBufferSize,
  504. IN LPCWSTR pwszLocalName,
  505. IN LPCWSTR pwszRemoteName,
  506. IN DWORD dwDeviceType,
  507. IN DWORD dwFlags
  508. )
  509. {
  510. _bPicked = FALSE;
  511. _dwError = WN_SUCCESS;
  512. //
  513. // If out pointers are supplied, make sure they're writeable
  514. // (even if we don't use them)
  515. //
  516. if (ARGUMENT_PRESENT(lpcchBufferSize))
  517. {
  518. _cchBufferSize = *(volatile DWORD *)lpcchBufferSize; // Probe
  519. *(volatile DWORD *)lpcchBufferSize = _cchBufferSize; // Probe
  520. }
  521. else
  522. {
  523. _cchBufferSize = 0;
  524. }
  525. if (ARGUMENT_PRESENT(lpAccessName))
  526. {
  527. //
  528. // If an AccessName buffer is supplied, the size parameter must
  529. // be present and non-zero, else return right away
  530. //
  531. if (_cchBufferSize == 0)
  532. {
  533. return WN_BAD_VALUE; // Win95 compatibility
  534. }
  535. if (IsBadWritePtr(lpAccessName, _cchBufferSize * sizeof(WCHAR)))
  536. {
  537. return WN_BAD_POINTER;
  538. }
  539. _cchReqBufferSize = 0;
  540. //
  541. // If an access name is requested, and a local name is
  542. // specified, then we know that the access name will be the
  543. // specified local name, so we can validate the buffer
  544. // length up front
  545. //
  546. if (! IS_EMPTY_STRING(pwszLocalName))
  547. {
  548. _cchReqBufferSize = wcslen(pwszLocalName)+1;
  549. }
  550. //
  551. // If an access name is requested, and no local name is
  552. // specified, and we aren't asked to auto-pick a local
  553. // device, then the access name is likely to be the specified
  554. // remote name, so we validate the buffer length up front
  555. //
  556. else if ((dwFlags & CONNECT_REDIRECT) == 0)
  557. {
  558. _cchReqBufferSize = wcslen(pwszRemoteName)+1;
  559. }
  560. if (_cchBufferSize < _cchReqBufferSize) {
  561. *lpcchBufferSize = _cchReqBufferSize;
  562. return WN_MORE_DATA;
  563. }
  564. //
  565. // Save other parameters for use in PickDevice()
  566. //
  567. _dwDeviceType = dwDeviceType;
  568. //
  569. // (If we auto-pick a local device, the buffer length will
  570. // be validated later, when we auto-pick)
  571. //
  572. }
  573. else
  574. {
  575. //
  576. // We won't be able to autopick, because no access name buffer
  577. // is supplied. But don't return the error until we're asked
  578. // to autopick.
  579. //
  580. _bPicked = TRUE;
  581. _dwError = WN_BAD_POINTER;
  582. }
  583. return WN_SUCCESS;
  584. }
  585. /*************************************************************************
  586. NAME: CAutoPickedDevice::PickDevice
  587. **************************************************************************/
  588. DWORD CAutoPickedDevice::PickDevice()
  589. {
  590. //
  591. // If we've been called before then return the error we returned
  592. // last time
  593. //
  594. if (_bPicked)
  595. {
  596. return _dwError;
  597. }
  598. _bPicked = TRUE;
  599. //
  600. // Validate the access name buffer size depending on the device type
  601. //
  602. if (_dwDeviceType == RESOURCETYPE_DISK)
  603. {
  604. _cchReqBufferSize = sizeof( "A:" );
  605. }
  606. else if (_dwDeviceType == RESOURCETYPE_PRINT)
  607. {
  608. _cchReqBufferSize = sizeof( "LPT1" );
  609. }
  610. else // RESOURCETYPE_ANY -- can't autopick
  611. {
  612. _dwError = WN_BAD_VALUE;
  613. return _dwError;
  614. }
  615. if ( _cchBufferSize < _cchReqBufferSize )
  616. {
  617. _dwError = WN_MORE_DATA;
  618. return _dwError;
  619. }
  620. if (_dwDeviceType == RESOURCETYPE_DISK)
  621. {
  622. DWORD dwDrives = GetLogicalDrives();
  623. DWORD dwAvail = (1 << ('Z' - 'A'));
  624. WCHAR i;
  625. //
  626. // Try each drive from z: to c: and see if it's been used.
  627. // Check in reverse order to reduce the risk of conflicts
  628. // between remote and local drive letters (e.g., if hardware
  629. // is added after a drive is mapped).
  630. //
  631. for (i = L'Z'; i >= L'C'; i--, dwDrives <<= 1)
  632. {
  633. if (!(dwDrives & dwAvail))
  634. {
  635. //
  636. // This drive is available
  637. //
  638. _wszPickedName[0] = i;
  639. _wszPickedName[1] = L':';
  640. _wszPickedName[2] = L'\0';
  641. _dwError = WN_SUCCESS;
  642. return _dwError;
  643. }
  644. }
  645. }
  646. else // (_dwDeviceType == RESOURCETYPE_PRINT)
  647. {
  648. WCHAR wszDeviceList[128];
  649. DWORD cch;
  650. //
  651. // Try each device from LPT1 to LPT99 and see if it's been used
  652. //
  653. wcscpy(_wszPickedName, L"LPT");
  654. for (ULONG i = 1; i <= 99; i++)
  655. {
  656. wsprintf(&_wszPickedName[sizeof("LPT")-1], L"%lu", i);
  657. cch = QueryDosDevice(_wszPickedName,
  658. wszDeviceList,
  659. sizeof(wszDeviceList)/sizeof(WCHAR));
  660. if ((cch == 0) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
  661. {
  662. //
  663. // QueryDosDevice failed -- this device is free
  664. //
  665. _dwError = WN_SUCCESS;
  666. return _dwError;
  667. }
  668. }
  669. }
  670. //
  671. // No unused devices found
  672. //
  673. _dwError = WN_NO_MORE_DEVICES;
  674. return _dwError;
  675. }
  676. //===================================================================
  677. // WNetUseConnectionW
  678. //===================================================================
  679. class CUseConnection : public CRoutedOperation
  680. {
  681. public:
  682. CUseConnection(
  683. HWND hwndOwner,
  684. LPNETRESOURCEW lpNetResource,
  685. LPCWSTR lpPassword,
  686. LPCWSTR lpUserName,
  687. DWORD dwFlags,
  688. LPWSTR lpAccessName,
  689. LPDWORD lpBufferSize,
  690. LPDWORD lpResult
  691. ) :
  692. CRoutedOperation(
  693. DBGPARM("UseConnection")
  694. NULL,
  695. ROUTE_AGGRESSIVE
  696. ),
  697. _hwndOwner (hwndOwner ),
  698. _lpNetResource(lpNetResource),
  699. _lpPassword (lpPassword ),
  700. _lpUserName (lpUserName ),
  701. _dwFlags (dwFlags ),
  702. _lpAccessName (lpAccessName ),
  703. _lpBufferSize (lpBufferSize ),
  704. _lpResult (lpResult )
  705. {
  706. _ExplicitPassword = (_lpPassword != NULL);
  707. _UsedDefaultCreds = FALSE;
  708. }
  709. protected:
  710. DWORD GetResult(); // overrides CRoutedOperation implementation
  711. private:
  712. DWORD TestProviderWorker( \
  713. const PROVIDER *pProvider \
  714. );
  715. HWND _hwndOwner;
  716. LPNETRESOURCEW _lpNetResource;
  717. LPCWSTR _lpPassword;
  718. LPCWSTR _lpUserName;
  719. DWORD _dwFlags;
  720. LPWSTR _lpAccessName;
  721. LPDWORD _lpBufferSize;
  722. LPDWORD _lpResult;
  723. BOOL _ExplicitPassword;
  724. BOOL _UsedDefaultCreds;
  725. CAutoPickedDevice _AutoPickedDevice;
  726. NETRESOURCEW _ProviderNetResource;
  727. DWORD _dwProviderFlags;
  728. DECLARE_CROUTED
  729. };
  730. DWORD
  731. CUseConnection::ValidateRoutedParameters(
  732. LPCWSTR * ppProviderName,
  733. LPCWSTR * ppRemoteName,
  734. LPCWSTR * ppLocalName
  735. )
  736. {
  737. DWORD status;
  738. //
  739. // lpRemoteName must be non-empty and readable.
  740. //
  741. if (wcslen(_lpNetResource->lpRemoteName) == 0) // Probe
  742. {
  743. return WN_BAD_NETNAME;
  744. }
  745. if (! IS_EMPTY_STRING(_lpNetResource->lpLocalName))
  746. {
  747. //
  748. // If a lpLocalName is supplied, it must be a redirectable device
  749. // name.
  750. //
  751. if (MprDeviceType(_lpNetResource->lpLocalName) != REDIR_DEVICE)
  752. {
  753. return WN_BAD_LOCALNAME;
  754. }
  755. }
  756. //
  757. // If a result pointer is supplied, make sure it's writeable
  758. //
  759. if (ARGUMENT_PRESENT(_lpResult))
  760. {
  761. *_lpResult = 0;
  762. }
  763. if ((_lpNetResource->dwType != RESOURCETYPE_DISK) &&
  764. (_lpNetResource->dwType != RESOURCETYPE_PRINT) &&
  765. (_lpNetResource->dwType != RESOURCETYPE_ANY))
  766. {
  767. return WN_BAD_VALUE;
  768. }
  769. if (_dwFlags &
  770. ~(CONNECT_TEMPORARY |
  771. CONNECT_INTERACTIVE |
  772. CONNECT_COMMANDLINE |
  773. CONNECT_CMD_SAVECRED |
  774. CONNECT_PROMPT |
  775. CONNECT_UPDATE_PROFILE |
  776. CONNECT_UPDATE_RECENT |
  777. CONNECT_REDIRECT |
  778. CONNECT_CURRENT_MEDIA))
  779. {
  780. return WN_BAD_VALUE;
  781. }
  782. //
  783. // Win95 compatibility: Ignore CONNECT_PROMPT if CONNECT_INTERACTIVE
  784. // isn't set
  785. //
  786. if (!(_dwFlags & CONNECT_INTERACTIVE))
  787. {
  788. _dwFlags &= ~CONNECT_PROMPT;
  789. //
  790. // Ditch the other prompting bits just to prevent later confusion
  791. //
  792. _dwFlags &= ~(CONNECT_COMMANDLINE|CONNECT_CMD_SAVECRED);
  793. }
  794. //
  795. // Validate parameters related to auto-picking of local device names.
  796. // Some errors are returned immediately; others are returned only
  797. // when we actually need to auto-pick a device.
  798. //
  799. status = _AutoPickedDevice.Init(
  800. _lpAccessName,
  801. _lpBufferSize,
  802. _lpNetResource->lpLocalName,
  803. _lpNetResource->lpRemoteName,
  804. _lpNetResource->dwType,
  805. _dwFlags
  806. );
  807. if (status != WN_SUCCESS)
  808. {
  809. return status;
  810. }
  811. //
  812. // Set parameters used by base class. Set the local name to NULL
  813. // to prevent the check for "localness" in the base class.
  814. //
  815. *ppProviderName = _lpNetResource->lpProvider;
  816. *ppRemoteName = _lpNetResource->lpRemoteName;
  817. *ppLocalName = NULL;
  818. return WN_SUCCESS;
  819. }
  820. DWORD
  821. CUseConnection::TestProviderWorker(
  822. const PROVIDER * pProvider
  823. )
  824. {
  825. DWORD status;
  826. ASSERT_INITIALIZED(NETWORK);
  827. if (pProvider->AddConnection3 == NULL &&
  828. pProvider->AddConnection == NULL)
  829. {
  830. return WN_NOT_SUPPORTED;
  831. }
  832. //
  833. // We will retry this provider, with an autopicked local name,
  834. // at most once
  835. //
  836. for (BOOL fRetried = FALSE; ; fRetried = TRUE)
  837. {
  838. //
  839. // Call the provider's appropriate entry point
  840. //
  841. //
  842. // If, in the future, there are cases when the lpRemoteName
  843. // is some alias understood by the provider but not by the
  844. // NT name space, we'll need to add a NPAddConnection4 which
  845. // allows the provider to return an access name.
  846. //
  847. if (pProvider->AddConnection3 != NULL)
  848. {
  849. //**************************************
  850. // Actual call to Provider.
  851. //**************************************
  852. status = pProvider->AddConnection3(
  853. _hwndOwner, // hwndOwner
  854. &_ProviderNetResource, // lpNetResource
  855. (LPWSTR)_lpPassword, // lpPassword
  856. (LPWSTR)_lpUserName, // lpUserName
  857. _dwProviderFlags & pProvider->ConnectFlagCaps ); // dwFlags
  858. if (status == WN_CONNECTED_OTHER_PASSWORD)
  859. {
  860. //
  861. // The user had to be prompted for a password, so don't
  862. // defer the connection when restoring it at the next logon.
  863. //
  864. ASSERT(_dwProviderFlags & CONNECT_INTERACTIVE);
  865. _ExplicitPassword = TRUE;
  866. status = WN_SUCCESS;
  867. }
  868. else if (status == WN_CONNECTED_OTHER_PASSWORD_DEFAULT)
  869. {
  870. //
  871. // The provider successfully used the default credentials
  872. // for this connection.
  873. //
  874. _UsedDefaultCreds = TRUE;
  875. status = WN_SUCCESS;
  876. }
  877. }
  878. else // (pProvider->AddConnection != NULL)
  879. {
  880. //**************************************
  881. // Actual call to Provider.
  882. //**************************************
  883. status = pProvider->AddConnection(
  884. &_ProviderNetResource, // lpNetResource
  885. (LPWSTR)_lpPassword, // lpPassword
  886. (LPWSTR)_lpUserName); // lpUserName
  887. ASSERT(status != WN_CONNECTED_OTHER_PASSWORD);
  888. }
  889. // The provider mustn't return this error, or it will mess us
  890. // up later
  891. ASSERT(status != WN_MORE_DATA);
  892. if (fRetried)
  893. {
  894. if (status != WN_SUCCESS)
  895. {
  896. // Restore the null local name
  897. _ProviderNetResource.lpLocalName = NULL;
  898. }
  899. break;
  900. }
  901. //
  902. // If this provider can't handle a NULL local name,
  903. // and we've been allowed to auto-pick a local name,
  904. // and we successfully auto-pick a local name,
  905. // try again with the auto-picked local name
  906. //
  907. // Note that status is updated iff we've been allowed
  908. // to auto-pick
  909. //
  910. if (status == WN_BAD_LOCALNAME &&
  911. _ProviderNetResource.lpLocalName == NULL &&
  912. _lpAccessName != NULL &&
  913. (status = _AutoPickedDevice.PickDevice()) == WN_SUCCESS)
  914. {
  915. _ProviderNetResource.lpLocalName =
  916. _AutoPickedDevice.wszPickedName();
  917. MPR_LOG2(ROUTE, "CUseConnection: retrying %ws with device %ws ...\n",
  918. pProvider->Resource.lpProvider,
  919. _ProviderNetResource.lpLocalName);
  920. }
  921. else
  922. {
  923. //
  924. // Don't retry
  925. //
  926. break;
  927. }
  928. } // end retry loop for this provider
  929. return status;
  930. }
  931. BOOL
  932. InitCredUI(
  933. IN PWSTR pszRemoteName,
  934. OUT PWSTR pszServer,
  935. IN ULONG ulServerLength
  936. )
  937. {
  938. // Make sure the first 2 characters are path separators:
  939. if ((pszRemoteName == NULL) ||
  940. (pszRemoteName[0] != L'\\') ||
  941. (pszRemoteName[1] != L'\\'))
  942. {
  943. SetLastError(WN_BAD_NETNAME);
  944. return FALSE;
  945. }
  946. PWSTR pszStart = pszRemoteName + 2;
  947. PWSTR pszEnd = NULL;
  948. // send only the server name, the string up to the first slash
  949. pszEnd = wcschr(pszStart, L'\\');
  950. if (pszEnd == NULL)
  951. {
  952. pszEnd = pszStart + wcslen(pszStart);
  953. }
  954. DWORD_PTR dwLength = (DWORD_PTR)(pszEnd - pszStart);
  955. if ((dwLength == 0) || (dwLength >= ulServerLength))
  956. {
  957. // The server is either an empty string or more than the maximum
  958. // number of characters we support:
  959. SetLastError(WN_BAD_NETNAME);
  960. return FALSE;
  961. }
  962. wcsncpy(pszServer, pszStart, dwLength);
  963. pszServer[dwLength] = L'\0';
  964. return TRUE;
  965. }
  966. VOID
  967. DisplayFailureReason(
  968. DWORD Status,
  969. LPCWSTR Path
  970. )
  971. /*++
  972. Routine Description:
  973. This routine displays the reason for the authentication failure.
  974. The complete path name is displayed.
  975. We could modify credui to display this, but credui doesn't have the full path name.
  976. Arguments:
  977. Status - Status of the failure
  978. Path - Full path name of the failed authentication.
  979. Return Value:
  980. None.
  981. --*/
  982. {
  983. DWORD MessageId;
  984. LPWSTR InsertionStrings[2];
  985. HMODULE lpSource = NULL;
  986. DWORD MessageLength = 0;
  987. LPWSTR Buffer = NULL;
  988. BOOL MessageFormatted = FALSE;
  989. //
  990. // Pick the message based in the failure status
  991. //
  992. MessageId = ( Status == ERROR_LOGON_FAILURE) ? APE_UseBadPassOrUser : APE_UseBadPass;
  993. InsertionStrings[0] = (LPWSTR) Path;
  994. //
  995. // Load the message library and format the message from it
  996. //
  997. lpSource = LoadLibraryEx(MESSAGE_FILENAME,
  998. NULL,
  999. LOAD_LIBRARY_AS_DATAFILE);
  1000. if ( lpSource ) {
  1001. //
  1002. // Format the message
  1003. //
  1004. MessageLength = FormatMessageW( FORMAT_MESSAGE_ARGUMENT_ARRAY |
  1005. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  1006. FORMAT_MESSAGE_FROM_HMODULE,
  1007. lpSource,
  1008. MessageId,
  1009. 0, // LanguageId defaulted
  1010. (LPWSTR) &Buffer,
  1011. 1024, // Limit the buffer size
  1012. (va_list *) InsertionStrings);
  1013. if ( MessageLength ) {
  1014. MessageFormatted = TRUE;
  1015. }
  1016. }
  1017. //
  1018. // If it failed, print a generic message
  1019. //
  1020. if ( !MessageFormatted ) {
  1021. WCHAR NumberString[18];
  1022. //
  1023. // get the message number in Unicode
  1024. //
  1025. _ultow(MessageId, NumberString, 16);
  1026. //
  1027. // setup insert strings
  1028. //
  1029. InsertionStrings[0] = NumberString;
  1030. InsertionStrings[1] = MESSAGE_FILENAME;
  1031. //
  1032. // Use the system messge file.
  1033. //
  1034. MessageLength = FormatMessageW( FORMAT_MESSAGE_ARGUMENT_ARRAY |
  1035. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  1036. FORMAT_MESSAGE_FROM_SYSTEM,
  1037. NULL,
  1038. ERROR_MR_MID_NOT_FOUND,
  1039. 0, // LanguageId defaulted
  1040. (LPWSTR) &Buffer,
  1041. 1024,
  1042. (va_list *) InsertionStrings);
  1043. }
  1044. //
  1045. // Avoid double newlines
  1046. //
  1047. if ( MessageLength >= 2) {
  1048. if ( Buffer[MessageLength - 1] == L'\n' &&
  1049. Buffer[MessageLength - 2] == L'\r') {
  1050. //
  1051. // "\r\n" shows up as two newlines when using the CRT and piping
  1052. // output to a file. Make it just one newline.
  1053. //
  1054. Buffer[MessageLength - 1] = L'\0';
  1055. Buffer[MessageLength - 2] = L'\n';
  1056. MessageLength --;
  1057. }
  1058. }
  1059. //
  1060. // Output the message to stdout
  1061. //
  1062. if ( MessageLength > 0 ) {
  1063. HANDLE StdoutHandle = GetStdHandle( STD_OUTPUT_HANDLE );
  1064. if ( StdoutHandle != INVALID_HANDLE_VALUE ) {
  1065. DWORD CharsWritten;
  1066. if ( !WriteConsoleW( StdoutHandle,
  1067. Buffer,
  1068. MessageLength,
  1069. &CharsWritten,
  1070. NULL )) {
  1071. //
  1072. // Convert to ANSI before doing the write file
  1073. //
  1074. if ( NO_ERROR == OutputStringToAnsiInPlace( Buffer ) ) {
  1075. CharsWritten = strlen( (LPSTR) Buffer );
  1076. WriteFile( StdoutHandle,
  1077. Buffer,
  1078. CharsWritten,
  1079. &CharsWritten,
  1080. NULL );
  1081. }
  1082. }
  1083. //
  1084. // Append a newline for formatting reasons
  1085. //
  1086. Buffer[0] = '\n';
  1087. WriteFile( StdoutHandle,
  1088. Buffer,
  1089. 1,
  1090. &CharsWritten,
  1091. NULL );
  1092. }
  1093. }
  1094. //
  1095. // Clean up before exitting
  1096. //
  1097. if ( lpSource != NULL ) {
  1098. FreeLibrary( lpSource );
  1099. }
  1100. if ( Buffer != NULL ) {
  1101. LocalFree( Buffer );
  1102. }
  1103. }
  1104. DWORD
  1105. CUseConnection::TestProvider(
  1106. const PROVIDER * pProvider
  1107. )
  1108. {
  1109. DWORD dwErr;
  1110. DWORD LocalProviderFlags;
  1111. BOOL DoCommandLinePrompting = FALSE;
  1112. //
  1113. // If the caller asked for CONNECT_COMMANDLINE,
  1114. // and the provider doesn't support it,
  1115. // this routine should take over responsibility for doing it.
  1116. //
  1117. LocalProviderFlags = _dwProviderFlags;
  1118. if ( (_dwProviderFlags & CONNECT_COMMANDLINE) != 0 &&
  1119. (pProvider->ConnectFlagCaps & CONNECT_COMMANDLINE) == 0 ) {
  1120. DoCommandLinePrompting = TRUE;
  1121. //
  1122. // Don't let the provider do GUI prompting.
  1123. //
  1124. _dwProviderFlags &= ~(CONNECT_INTERACTIVE | CONNECT_PROMPT | CONNECT_COMMANDLINE | CONNECT_CMD_SAVECRED);
  1125. }
  1126. //
  1127. // Try the connection once
  1128. // (Unless we're asked not to before prompting.)
  1129. //
  1130. if ( !DoCommandLinePrompting || (LocalProviderFlags & CONNECT_PROMPT) == 0 ) {
  1131. dwErr = TestProviderWorker( pProvider );
  1132. } else {
  1133. dwErr = ERROR_LOGON_FAILURE;
  1134. }
  1135. //
  1136. // If this routine is handling command line prompting,
  1137. // and the connection failed for authentication reasons,
  1138. // prompt for credentials here.
  1139. //
  1140. // Ignore COMMENT_COMMANDLINE_SAVECRED since we don't know what authentication mechanism
  1141. // the provider uses.
  1142. //
  1143. if ( DoCommandLinePrompting && CREDUI_IS_AUTHENTICATION_ERROR(dwErr) )
  1144. {
  1145. WCHAR szPassword[CREDUI_MAX_PASSWORD_LENGTH + 1]; // if the user needs to enter one
  1146. WCHAR szUserName[CREDUI_MAX_USERNAME_LENGTH + 1];
  1147. WCHAR szServer[CRED_MAX_DOMAIN_TARGET_NAME_LENGTH + 1];
  1148. //
  1149. // Display the reason for the failure with the full path name
  1150. //
  1151. DisplayFailureReason( dwErr, _ProviderNetResource.lpRemoteName );
  1152. //
  1153. // Default the user name to the one provided by the caller.
  1154. //
  1155. szUserName[0] = L'\0';
  1156. if ( _lpUserName != NULL ) {
  1157. if (wcslen(_lpUserName) > CREDUI_MAX_USERNAME_LENGTH) {
  1158. return WN_BAD_USER;
  1159. }
  1160. wcscpy(szUserName, _lpUserName);
  1161. }
  1162. //
  1163. // Prepare to use the credential manager user interface:
  1164. //
  1165. if (!InitCredUI(_ProviderNetResource.lpRemoteName,
  1166. szServer,
  1167. CRED_MAX_DOMAIN_TARGET_NAME_LENGTH))
  1168. {
  1169. dwErr = GetLastError();
  1170. }
  1171. else
  1172. {
  1173. DWORD dwCredErr;
  1174. DWORD dwAuthErr;
  1175. LPWSTR pszNewPassword;
  1176. DWORD dwCredUIFlags = 0;
  1177. //
  1178. // Remember the original failure reason
  1179. //
  1180. dwAuthErr = dwErr;
  1181. //
  1182. // Set the appropriate flag to set the behavior of the common UI.
  1183. //
  1184. // Ask to not save the credential
  1185. dwCredUIFlags |= CREDUI_FLAGS_DO_NOT_PERSIST |
  1186. CREDUI_FLAGS_GENERIC_CREDENTIALS;
  1187. // We don't (yet) know how to handle certificates
  1188. dwCredUIFlags |= CREDUI_FLAGS_EXCLUDE_CERTIFICATES;
  1189. // Ensure that the username syntax is correct (Not in MPR)
  1190. // dwCredUIFlags |= CREDUI_FLAGS_VALIDATE_USERNAME;
  1191. dwCredErr = CredUICmdLinePromptForCredentials(
  1192. szServer,
  1193. NULL,
  1194. dwAuthErr,
  1195. szUserName,
  1196. CREDUI_MAX_USERNAME_LENGTH,
  1197. szPassword,
  1198. CREDUI_MAX_PASSWORD_LENGTH,
  1199. NULL, // Creds not saved
  1200. dwCredUIFlags);
  1201. if (dwCredErr == ERROR_CANCELLED) {
  1202. dwErr = WN_CANCEL;
  1203. } else if ( dwCredErr == ERROR_SUCCESS ) {
  1204. LPCWSTR OldPassword;
  1205. //
  1206. // Use the returned username and password
  1207. //
  1208. _lpUserName = szUserName;
  1209. OldPassword = _lpPassword;
  1210. _lpPassword = szPassword;
  1211. //
  1212. // Try the connection one more time
  1213. //
  1214. dwErr = TestProviderWorker( pProvider );
  1215. if ( dwErr == NO_ERROR )
  1216. {
  1217. //
  1218. // If typed password is different than the one passed in,
  1219. // this is now an explicit password.
  1220. //
  1221. if ( OldPassword == NULL ||
  1222. wcscmp(OldPassword, _lpPassword) != 0 )
  1223. {
  1224. _ExplicitPassword = TRUE;
  1225. }
  1226. }
  1227. }
  1228. //
  1229. // clear any password from memory
  1230. //
  1231. ZeroMemory(szPassword, sizeof(szPassword)) ;
  1232. }
  1233. }
  1234. //
  1235. // Restore the flag to their initial value
  1236. //
  1237. _dwProviderFlags = LocalProviderFlags;
  1238. return dwErr;
  1239. }
  1240. DWORD
  1241. CUseConnection::GetResult()
  1242. {
  1243. DWORD status;
  1244. //
  1245. // If we're given a local name,
  1246. // check the current list of remembered drives in the registry
  1247. // to determine if the localName is already connected.
  1248. //
  1249. if (! IS_EMPTY_STRING(_lpNetResource->lpLocalName))
  1250. {
  1251. //
  1252. // If the local drive is already in the registry, and it is
  1253. // for a different connection than that specified in the
  1254. // lpNetResource, then indicate an error because the device
  1255. // is already remembered.
  1256. //
  1257. LPWSTR remoteName;
  1258. if (MprFindDriveInRegistry(_lpNetResource->lpLocalName, &remoteName))
  1259. {
  1260. if (remoteName != NULL)
  1261. {
  1262. if (STRICMP(_lpNetResource->lpRemoteName, remoteName)!=0)
  1263. {
  1264. LocalFree(remoteName);
  1265. return WN_DEVICE_ALREADY_REMEMBERED;
  1266. }
  1267. LocalFree(remoteName);
  1268. }
  1269. }
  1270. }
  1271. //
  1272. // We modify some parameters before passing them to the provider
  1273. //
  1274. _ProviderNetResource = *_lpNetResource;
  1275. if (IS_EMPTY_STRING(_ProviderNetResource.lpLocalName))
  1276. {
  1277. _ProviderNetResource.lpLocalName = NULL;
  1278. }
  1279. _dwProviderFlags = _dwFlags & WNNC_CF_MAXIMUM;
  1280. //
  1281. // If we're not given a local name, but are explicitly asked to
  1282. // redirect a device, we must autopick one
  1283. //
  1284. if ((_dwFlags & CONNECT_REDIRECT) &&
  1285. (_ProviderNetResource.lpLocalName == NULL) )
  1286. {
  1287. status = _AutoPickedDevice.PickDevice();
  1288. if (status != WN_SUCCESS)
  1289. {
  1290. return status;
  1291. }
  1292. _ProviderNetResource.lpLocalName = _AutoPickedDevice.wszPickedName();
  1293. }
  1294. INIT_IF_NECESSARY(NOTIFIEE_LEVEL,status);
  1295. //
  1296. // Notify all interested parties that a connection is being made.
  1297. // (All providers are required to support deviceless connections, so
  1298. // we shouldn't get a case where a notifyee might reject a connection
  1299. // on the grounds that it has no local name, even though we would have
  1300. // auto-picked a local name later)
  1301. //
  1302. NOTIFYADD NotifyAdd;
  1303. NOTIFYINFO NotifyInfo;
  1304. NotifyInfo.dwNotifyStatus = NOTIFY_PRE;
  1305. NotifyInfo.dwOperationStatus= 0L;
  1306. NotifyInfo.lpContext = MprAllocConnectContext();
  1307. NotifyAdd.hwndOwner = _hwndOwner;
  1308. NotifyAdd.NetResource = *_lpNetResource;
  1309. NotifyAdd.dwAddFlags = _dwFlags;
  1310. __try
  1311. {
  1312. status = MprAddConnectNotify(&NotifyInfo, &NotifyAdd);
  1313. }
  1314. __except(EXCEPTION_EXECUTE_HANDLER)
  1315. {
  1316. status = GetExceptionCode();
  1317. if (status != EXCEPTION_ACCESS_VIOLATION)
  1318. {
  1319. MPR_LOG(ERROR,"WNetUseConnectionW: ConnectNotify, Unexpected "
  1320. "Exception %#lx\n",status);
  1321. }
  1322. status = WN_BAD_POINTER;
  1323. }
  1324. if (status != WN_SUCCESS)
  1325. {
  1326. return status;
  1327. }
  1328. do // Notification loop
  1329. {
  1330. //
  1331. // Let the base class try all providers and figure out the best error
  1332. // CRoutedOperation::GetResult calls INIT_IF_NECESSARY
  1333. //
  1334. status = CRoutedOperation::GetResult();
  1335. //
  1336. // Notify all interested parties of the status of the connection.
  1337. //
  1338. NotifyInfo.dwNotifyStatus = NOTIFY_POST;
  1339. NotifyInfo.dwOperationStatus = status;
  1340. }
  1341. while (MprAddConnectNotify(&NotifyInfo, &NotifyAdd) == WN_RETRY);
  1342. MprFreeConnectContext(NotifyInfo.lpContext);
  1343. //
  1344. // Write info to the informational parameters
  1345. // (lpAccessName, lpBufferSize, lpResult)
  1346. //
  1347. if (status == WN_MORE_DATA)
  1348. {
  1349. //
  1350. // This error must have come from CAutoPickedDevice::PickDevice,
  1351. // indicating that the access name buffer is too small
  1352. // (unless it came from a buggy provider)
  1353. //
  1354. ASSERT(_AutoPickedDevice.dwError() == WN_MORE_DATA);
  1355. if (ARGUMENT_PRESENT(_lpBufferSize))
  1356. {
  1357. *_lpBufferSize = _AutoPickedDevice.cchReqBufferSize();
  1358. }
  1359. }
  1360. else if (status == WN_SUCCESS &&
  1361. ARGUMENT_PRESENT(_lpAccessName))
  1362. {
  1363. LPWSTR pwszAccessName = _ProviderNetResource.lpLocalName;
  1364. if (pwszAccessName == NULL)
  1365. {
  1366. pwszAccessName = _ProviderNetResource.lpRemoteName;
  1367. }
  1368. if (*_lpBufferSize < wcslen(pwszAccessName)+1)
  1369. {
  1370. //
  1371. // We validated most of the cases up front, so the only way
  1372. // this could happen is if we auto-picked and got a local
  1373. // name that was longer than the remote name.
  1374. //
  1375. ASSERT(0);
  1376. *_lpBufferSize = wcslen(pwszAccessName)+1;
  1377. status = WN_MORE_DATA;
  1378. }
  1379. else
  1380. {
  1381. wcscpy(_lpAccessName, pwszAccessName);
  1382. if (ARGUMENT_PRESENT(_lpResult) &&
  1383. _ProviderNetResource.lpLocalName != NULL)
  1384. {
  1385. *_lpResult = CONNECT_LOCALDRIVE;
  1386. }
  1387. }
  1388. }
  1389. if (status == WN_SUCCESS)
  1390. {
  1391. ASSERT_INITIALIZED(NETWORK);
  1392. //
  1393. // If the connection was added successfully, then write the connection
  1394. // information to the registry to make it persistent.
  1395. // Note: Failure to write to the registry is ignored.
  1396. //
  1397. if ((_dwFlags & CONNECT_UPDATE_PROFILE) &&
  1398. !IS_EMPTY_STRING(_ProviderNetResource.lpLocalName))
  1399. {
  1400. BYTE ProviderFlags = 0;
  1401. //
  1402. // Get the username for the connection if the provider didn't report
  1403. // that default creds were used:
  1404. //
  1405. // 1. If the username is explicitly given, store the
  1406. // username returned by the provider
  1407. //
  1408. // 2. Otherwise, compare the username given by the
  1409. // provider with the default username. If they're
  1410. // different, store it. If they're the same, don't
  1411. //
  1412. // 3. If we can't get a username for whatever reason,
  1413. // use the username in _lpUserName (which will be
  1414. // NULL if no name was explicitly given)
  1415. //
  1416. if (LastProvider()->GetUser != NULL)
  1417. {
  1418. WCHAR wszUser[MAX_PATH + 1];
  1419. __try
  1420. {
  1421. DWORD cbBuffer = LENGTH(wszUser);
  1422. DWORD status2 = LastProvider()->GetUser(
  1423. _ProviderNetResource.lpLocalName,
  1424. wszUser,
  1425. &cbBuffer);
  1426. ASSERT(status2 != WN_MORE_DATA);
  1427. if (status2 == WN_SUCCESS)
  1428. {
  1429. //
  1430. // Step 1 -- is the username explicitly given?
  1431. //
  1432. if (ARGUMENT_PRESENT(_lpUserName))
  1433. {
  1434. MPR_LOG1(TRACE,
  1435. "Explicit username being saved -- %ws\n",
  1436. wszUser);
  1437. _lpUserName = wszUser;
  1438. }
  1439. else
  1440. {
  1441. //
  1442. // Step 2 -- is the user the default user?
  1443. //
  1444. PUNICODE_STRING UserName;
  1445. PUNICODE_STRING DomainName;
  1446. NTSTATUS ntStatus = LsaGetUserName(&UserName, &DomainName);
  1447. if (NT_SUCCESS(ntStatus))
  1448. {
  1449. if (MprUserNameMatch(DomainName, UserName, wszUser, TRUE))
  1450. {
  1451. MPR_LOG1(TRACE,
  1452. "User %ws is default user -- not saving username\n",
  1453. wszUser);
  1454. _lpUserName = NULL;
  1455. }
  1456. else
  1457. {
  1458. MPR_LOG1(TRACE,
  1459. "User %ws is not default user -- saving username\n",
  1460. wszUser);
  1461. _lpUserName = wszUser;
  1462. }
  1463. }
  1464. }
  1465. }
  1466. else if (status2 == WN_CONNECTED_OTHER_PASSWORD_DEFAULT)
  1467. {
  1468. //
  1469. // WN_CONNECTED_OTHER_PASSWORD_DEFAULT will be returned
  1470. // when user X mapped a drive as user Y and the credentials
  1471. // for user Y were stored in CredMan when the connection
  1472. // was made. Treat this as the default username so we don't
  1473. // explicitly write user Y's username into the registry
  1474. // (since CredMan will be unable to reestablish the connection
  1475. // automatically in that case).
  1476. //
  1477. _lpUserName = NULL;
  1478. }
  1479. }
  1480. __except (EXCEPTION_EXECUTE_HANDLER)
  1481. {
  1482. MPR_LOG(ERROR,
  1483. "WNetUseConnectionW: exception %#lx from NPGetUser\n",
  1484. GetExceptionCode());
  1485. }
  1486. }
  1487. //
  1488. // Get an 8-bit datum that the provider may want saved along
  1489. // with the connection. This will be passed back to the provider
  1490. // in NPAddConnection3 when the connection is restored.
  1491. // (Actually this is a hack for the NTLM provider to remember
  1492. // whether a connection is a DFS connection or a regular LM
  1493. // connection.)
  1494. //
  1495. if (LastProvider()->GetReconnectFlags != NULL)
  1496. {
  1497. // This is an internal entry point so we don't bother with
  1498. // try-except
  1499. DWORD status2 = LastProvider()->GetReconnectFlags(
  1500. _ProviderNetResource.lpLocalName,
  1501. &ProviderFlags
  1502. );
  1503. if (status2 != WN_SUCCESS)
  1504. {
  1505. ProviderFlags = 0;
  1506. }
  1507. MPR_LOG3(RESTORE, "%ws wants flags %#x saved for %ws\n",
  1508. LastProvider()->Resource.lpProvider,
  1509. ProviderFlags,
  1510. _ProviderNetResource.lpLocalName);
  1511. }
  1512. I_MprSaveConn(
  1513. HKEY_CURRENT_USER,
  1514. LastProvider()->Resource.lpProvider,
  1515. LastProvider()->Type,
  1516. _lpUserName,
  1517. _ProviderNetResource.lpLocalName,
  1518. _ProviderNetResource.lpRemoteName,
  1519. _ProviderNetResource.dwType,
  1520. ProviderFlags,
  1521. _ExplicitPassword ? DEFER_EXPLICIT_PASSWORD : ( _UsedDefaultCreds ? DEFER_DEFAULT_CRED : 0 )
  1522. );
  1523. }
  1524. //
  1525. // Notify the shell of the connection.
  1526. // (This will be replaced by real Plug'n'Play)
  1527. //
  1528. if( (g_LUIDDeviceMapsEnabled == TRUE) &&
  1529. (_ProviderNetResource.lpLocalName != NULL) )
  1530. {
  1531. // Use LUID broadcast mechanism
  1532. MprBroadcastDriveChange(
  1533. _ProviderNetResource.lpLocalName,
  1534. FALSE ); // not a Delete Message
  1535. }
  1536. else
  1537. {
  1538. MprNotifyShell(_ProviderNetResource.lpLocalName);
  1539. }
  1540. }
  1541. return status;
  1542. }
  1543. DWORD
  1544. WNetUseConnectionW (
  1545. IN HWND hwndOwner,
  1546. IN LPNETRESOURCEW lpNetResource,
  1547. IN LPCWSTR lpPassword,
  1548. IN LPCWSTR lpUserName,
  1549. IN DWORD dwFlags,
  1550. OUT LPWSTR lpAccessName OPTIONAL,
  1551. IN OUT LPDWORD lpBufferSize OPTIONAL,
  1552. OUT LPDWORD lpResult OPTIONAL
  1553. )
  1554. /*++
  1555. Routine Description:
  1556. This function allows the caller to redirect (connect) a local device
  1557. to a network resource. It is similar to WNetAddConnection3, except
  1558. that it has the ability to auto-pick a local device to redirect.
  1559. It also returns additional information about the connection in the
  1560. lpResult parameter.
  1561. This API is used by the shell to make links to:
  1562. - Objects on networks that require a local device redirection
  1563. - Objects served by applications that require a local device redirection
  1564. (i.e. can't handle UNC paths)
  1565. This API must redirect a local device in any of the following conditions:
  1566. - The API is given a local device name to redirect
  1567. - The API is asked to redirect a local device, by the CONNECT_REDIRECT
  1568. flag
  1569. - The network provider requires a redirection
  1570. In the last 2 cases, if a local device name isn't given, the API must
  1571. auto-pick a local device name.
  1572. Arguments:
  1573. hwndOwner - A handle to a window which should be the owner for any
  1574. messages or dialogs that the network provider might display.
  1575. lpNetResource - This is a pointer to a network resource structure that
  1576. specifies the network resource to connect to. The following
  1577. fields must be set when making a connection, the others are ignored.
  1578. lpRemoteName
  1579. lpLocalName
  1580. lpProvider
  1581. dwType
  1582. lpPassword - Specifies the password to be used in making the connection.
  1583. The NULL value may be passed in to indicate use of the 'default'
  1584. password. An empty string may be used to indicate no password.
  1585. lpUserName- This specifies the username used to make the connection.
  1586. If NULL, the default username (currently logged on user) will be
  1587. applied. This is used when the user wishes to connect to a
  1588. resource, but has a different user name or account assigned to him
  1589. for that resource.
  1590. dwFlags - This is a bitmask which may have any of the following bits set:
  1591. CONNECT_UPDATE_PROFILE
  1592. lpAccessName - Points to a buffer to receive the name that can be used
  1593. to make system requests on the connection. This conversion is useful
  1594. when lpRemoteName is understood by the provider but not by the
  1595. system's name space, and when this API autopicks a local device.
  1596. If lpLocalName specifies a local device, then this buffer is optional,
  1597. and if specified will have the local device name copied into it.
  1598. Otherwise, if the network requires a local device redirection, or
  1599. CONNECT_REDIRECT is set, then this buffer is required and the
  1600. redirected local device is returned here. Otherwise, the name copied
  1601. into the buffer is that of a remote resource, and if specified, this
  1602. buffer must be at least as large as the string pointed to by
  1603. lpRemoteName.
  1604. lpBufferSize - Specifies the size, in characters, of the lpAccessName
  1605. buffer. If the API returns WN_MORE_DATA the required size will be
  1606. returned here.
  1607. lpResult - Pointer to a DWORD in which is returned additional information
  1608. about the connection. Currently has the following bit values:
  1609. CONNECT_LOCALDRIVE - If set, the connection was made using a local
  1610. device redirection. If lpAccessName points to a buffer then the
  1611. local device name is copied to the buffer.
  1612. Return Value:
  1613. --*/
  1614. {
  1615. CUseConnection UseConnection(hwndOwner, lpNetResource, lpPassword,
  1616. lpUserName, dwFlags, lpAccessName,
  1617. lpBufferSize, lpResult);
  1618. return (UseConnection.Perform(TRUE));
  1619. }
  1620. //===================================================================
  1621. // WNetCancelConnection2W
  1622. //===================================================================
  1623. class CCancelConnection2 : public CRoutedOperation
  1624. {
  1625. public:
  1626. CCancelConnection2(
  1627. LPCWSTR lpName,
  1628. DWORD dwFlags,
  1629. BOOL fForce
  1630. ) :
  1631. CRoutedOperation(DBGPARM("CancelConnection2")
  1632. PROVIDERFUNC(CancelConnection)),
  1633. _lpName (lpName),
  1634. _dwFlags(dwFlags),
  1635. _fForce (fForce)
  1636. { }
  1637. protected:
  1638. DWORD GetResult(); // overrides CRoutedOperation implementation
  1639. private:
  1640. LPCWSTR _lpName;
  1641. DWORD _dwFlags;
  1642. BOOL _fForce;
  1643. DECLARE_CROUTED
  1644. };
  1645. DWORD
  1646. CCancelConnection2::ValidateRoutedParameters(
  1647. LPCWSTR * ppProviderName,
  1648. LPCWSTR * ppRemoteName,
  1649. LPCWSTR * ppLocalName
  1650. )
  1651. {
  1652. if (((_dwFlags != 0) && (_dwFlags != CONNECT_UPDATE_PROFILE)) ||
  1653. wcslen(_lpName) == 0) // probe lpName
  1654. {
  1655. return WN_BAD_VALUE;
  1656. }
  1657. //
  1658. // Use the specified remote name as a hint to pick the provider, but not
  1659. // if it's a local device name
  1660. //
  1661. if (MprDeviceType(_lpName) != REDIR_DEVICE)
  1662. {
  1663. *ppRemoteName = _lpName;
  1664. }
  1665. *ppLocalName = _lpName;
  1666. return WN_SUCCESS;
  1667. }
  1668. DWORD
  1669. CCancelConnection2::TestProvider(
  1670. const PROVIDER * pProvider
  1671. )
  1672. {
  1673. ASSERT_INITIALIZED(NETWORK);
  1674. return (pProvider->CancelConnection((LPWSTR)_lpName, _fForce));
  1675. }
  1676. DWORD
  1677. CCancelConnection2::GetResult()
  1678. {
  1679. DWORD status;
  1680. NOTIFYCANCEL NotifyCancel;
  1681. NOTIFYINFO NotifyInfo;
  1682. INIT_IF_NECESSARY(NOTIFIEE_LEVEL, status);
  1683. //
  1684. // Notify all interested parties that a connection is being cancelled
  1685. //
  1686. NotifyInfo.dwNotifyStatus = NOTIFY_PRE;
  1687. NotifyInfo.dwOperationStatus= 0L;
  1688. NotifyInfo.lpContext = MprAllocConnectContext();
  1689. NotifyCancel.lpName = (LPWSTR)_lpName;
  1690. NotifyCancel.lpProvider = NULL;
  1691. NotifyCancel.dwFlags = _dwFlags;
  1692. NotifyCancel.fForce = _fForce;
  1693. __try
  1694. {
  1695. status = MprCancelConnectNotify(&NotifyInfo, &NotifyCancel);
  1696. }
  1697. __except(EXCEPTION_EXECUTE_HANDLER)
  1698. {
  1699. status = GetExceptionCode();
  1700. if (status != EXCEPTION_ACCESS_VIOLATION)
  1701. {
  1702. MPR_LOG(ERROR,"WNetCancelConnection2W: ConnectNotify, Unexpected "
  1703. "Exception %#lx\n",status);
  1704. }
  1705. status = WN_BAD_POINTER;
  1706. }
  1707. if (status != WN_SUCCESS)
  1708. {
  1709. return status;
  1710. }
  1711. do // Notification loop
  1712. {
  1713. //
  1714. // Let the base class try all providers and figure out the best error
  1715. // CRoutedOperation::GetResult calls INIT_IF_NECESSARY
  1716. //
  1717. status = CRoutedOperation::GetResult();
  1718. //
  1719. // Map these errors to a friendlier one for this API
  1720. //
  1721. if (status == WN_BAD_NETNAME ||
  1722. status == ERROR_BAD_NETPATH ||
  1723. status == WN_BAD_LOCALNAME ||
  1724. status == WN_NO_NETWORK ||
  1725. status == WN_NO_NET_OR_BAD_PATH)
  1726. {
  1727. MPR_LOG2(ROUTE, "CCancelConnection2: remapping %ld to %ld\n",
  1728. status, WN_NOT_CONNECTED);
  1729. status = WN_NOT_CONNECTED;
  1730. }
  1731. //
  1732. // Notify all interested parties of the status of the connection.
  1733. //
  1734. NotifyInfo.dwNotifyStatus = NOTIFY_POST;
  1735. NotifyInfo.dwOperationStatus = status; // Is this the right status??
  1736. if (status == WN_SUCCESS)
  1737. {
  1738. NotifyCancel.lpProvider = LastProvider()->Resource.lpProvider;
  1739. }
  1740. }
  1741. while (MprCancelConnectNotify(&NotifyInfo, &NotifyCancel) == WN_RETRY);
  1742. MprFreeConnectContext(NotifyInfo.lpContext);
  1743. //
  1744. // Regardless of whether the connection was cancelled successfully,
  1745. // we still want to remove any connection information from the
  1746. // registry if told to do so (dwFlags has CONNECT_UPDATE_PROFILE set).
  1747. //
  1748. if (_dwFlags & CONNECT_UPDATE_PROFILE)
  1749. {
  1750. if (MprDeviceType((LPWSTR)_lpName) == REDIR_DEVICE)
  1751. {
  1752. if (MprFindDriveInRegistry((LPWSTR)_lpName,NULL))
  1753. {
  1754. //
  1755. // If the connection was found in the registry, we want to
  1756. // forget it and if no providers claimed responsibility,
  1757. // return success.
  1758. //
  1759. MprForgetRedirConnection((LPWSTR)_lpName);
  1760. if (status == WN_NOT_CONNECTED)
  1761. {
  1762. status = WN_SUCCESS;
  1763. }
  1764. }
  1765. }
  1766. }
  1767. if (status == WN_SUCCESS)
  1768. {
  1769. //
  1770. // Notify the shell of the disconnection.
  1771. //
  1772. if( (g_LUIDDeviceMapsEnabled == TRUE) && (_lpName != NULL) )
  1773. {
  1774. // Use LUID broadcast mechanism
  1775. MprBroadcastDriveChange(
  1776. _lpName,
  1777. TRUE ); // a Delete Message
  1778. }
  1779. else
  1780. {
  1781. MprNotifyShell(_lpName);
  1782. }
  1783. }
  1784. return status;
  1785. }
  1786. DWORD
  1787. WNetCancelConnection2W (
  1788. IN LPCWSTR lpName,
  1789. IN DWORD dwFlags,
  1790. IN BOOL fForce
  1791. )
  1792. /*++
  1793. Routine Description:
  1794. This function breaks an existing network connection. The persistance
  1795. of the connection is determined by the dwFlags parameter.
  1796. Arguments:
  1797. lpName - The name of either the redirected local device, or the remote
  1798. network resource to disconnect from. In the former case, only the
  1799. redirection specified is broken. In the latter case, all
  1800. connections to the remote network resource are broken.
  1801. dwFlags - This is a bitmask which may have any of the following bits set:
  1802. CONNECT_UPDATE_PROFILE
  1803. fForce - Used to indicate if the disconnect should be done forcefully
  1804. in the event of open files or jobs on the connection. If FALSE is
  1805. specified, the call will fail if there are open files or jobs.
  1806. Return Value:
  1807. WN_SUCCESS - The call was successful. Otherwise, GetLastError should be
  1808. called for extended error information. Extended error codes include
  1809. the following:
  1810. WN_NOT_CONNECTED - lpName is not a redirected device. or not currently
  1811. connected to lpName.
  1812. WN_OPEN_FILES - There are open files and fForce was FALSE.
  1813. WN_EXTENDED_ERROR - A network specific error occured. WNetGetLastError
  1814. should be called to obtain a description of the error.
  1815. --*/
  1816. {
  1817. //
  1818. // Check the providers
  1819. //
  1820. return MprCancelConnection2(lpName, dwFlags, fForce, TRUE);
  1821. }
  1822. DWORD
  1823. MprCancelConnection2 (
  1824. IN LPCWSTR lpName,
  1825. IN DWORD dwFlags,
  1826. IN BOOL fForce,
  1827. IN BOOL fCheckProviders
  1828. )
  1829. /*++
  1830. Routine Description:
  1831. Wrapper to use the CCancelConnection2 class -- needed because otherwise,
  1832. WNetRestoreConnectionW could call WNetCancelConnection2W, causing a
  1833. deadlock on the provider lock.
  1834. Arguments:
  1835. Return Value:
  1836. --*/
  1837. {
  1838. CCancelConnection2 CancelConnection2(lpName, dwFlags, fForce);
  1839. return (CancelConnection2.Perform(fCheckProviders));
  1840. }
  1841. DWORD
  1842. WNetCancelConnectionW (
  1843. IN LPCWSTR lpName,
  1844. IN BOOL fForce
  1845. )
  1846. /*++
  1847. Routine Description:
  1848. This function breaks an existing network connection. The connection is
  1849. always made non-persistent.
  1850. Note that this is a stub routine that calls WNetCancelConnection2W and
  1851. is only provided for Win3.1 compatibility.
  1852. Arguments:
  1853. Parameters are the same as WNetCancelConnection2W
  1854. Return Value:
  1855. Same as WNetCancelConnection2W
  1856. --*/
  1857. {
  1858. return MprCancelConnection2( lpName, CONNECT_UPDATE_PROFILE, fForce, TRUE ) ;
  1859. }
  1860. DWORD
  1861. WNetGetConnectionW (
  1862. IN LPCWSTR lpLocalName,
  1863. OUT LPWSTR lpRemoteName,
  1864. IN OUT LPDWORD lpBufferSize
  1865. )
  1866. /*++
  1867. Routine Description:
  1868. Arguments:
  1869. Return Value:
  1870. --*/
  1871. {
  1872. MprCheckProviders();
  1873. CProviderSharedLock PLock;
  1874. return MprGetConnection( lpLocalName, lpRemoteName, lpBufferSize, NULL ) ;
  1875. }
  1876. DWORD
  1877. MprGetConnection (
  1878. IN LPCWSTR lpLocalName,
  1879. OUT LPTSTR lpRemoteName,
  1880. IN OUT LPDWORD lpBufferSize,
  1881. OUT LPDWORD lpProviderIndex OPTIONAL
  1882. )
  1883. /*++
  1884. Routine Description:
  1885. Retrieves the remote name associated with a device name and optionally
  1886. the provider index.
  1887. Behaviour is exactly the same as WNetGetConnectionW.
  1888. Arguments:
  1889. Return Value:
  1890. --*/
  1891. {
  1892. DWORD status = WN_SUCCESS;
  1893. LPDWORD indexArray;
  1894. DWORD localArray[DEFAULT_MAX_PROVIDERS];
  1895. DWORD numProviders;
  1896. LPPROVIDER provider;
  1897. DWORD statusFlag = 0; // used to indicate major error types
  1898. BOOL fcnSupported = FALSE; // Is fcn supported by a provider?
  1899. DWORD i;
  1900. DWORD dwFirstError = WN_SUCCESS;
  1901. DWORD dwFirstSignificantError = WN_SUCCESS;
  1902. //
  1903. // Validate the LocalName
  1904. //
  1905. __try {
  1906. if (MprDeviceType((LPWSTR) lpLocalName) != REDIR_DEVICE) {
  1907. status = WN_BAD_LOCALNAME;
  1908. }
  1909. }
  1910. __except(EXCEPTION_EXECUTE_HANDLER) {
  1911. status = GetExceptionCode();
  1912. if (status != EXCEPTION_ACCESS_VIOLATION) {
  1913. MPR_LOG(ERROR,"WNetGetConnection:Unexpected Exception 0x%lx\n",status);
  1914. }
  1915. status = WN_BAD_POINTER;
  1916. }
  1917. if (status != WN_SUCCESS) {
  1918. SetLastError(status);
  1919. return status;
  1920. }
  1921. //
  1922. // Optimization: If the local name is a drive, load the providers
  1923. // only if it's a remote drive.
  1924. //
  1925. if (lpLocalName[1] == L':')
  1926. {
  1927. WCHAR wszRootPath[] = L" :\\";
  1928. UINT uDriveType;
  1929. wszRootPath[0] = lpLocalName[0];
  1930. uDriveType = GetDriveType(wszRootPath);
  1931. if (uDriveType == DRIVE_REMOVABLE ||
  1932. uDriveType == DRIVE_FIXED ||
  1933. uDriveType == DRIVE_CDROM ||
  1934. uDriveType == DRIVE_RAMDISK)
  1935. {
  1936. status = WN_NOT_CONNECTED;
  1937. SetLastError(status);
  1938. return status;
  1939. }
  1940. else if (uDriveType != DRIVE_REMOTE) {
  1941. //
  1942. // It's not a remote drive, but it's not one in hardware
  1943. // (either DRIVE_UNKNOWN or DRIVE_NO_ROOT_DIR). Since it
  1944. // might have a name associated with it in the registry
  1945. // (e.g., remembered connections), we have to load the
  1946. // providers and check if necessary (done in
  1947. // MprReadConnectionInformation)
  1948. //
  1949. status = WN_NOT_CONNECTED;
  1950. goto CheckRemembered;
  1951. }
  1952. }
  1953. INIT_IF_NECESSARY(NETWORK_LEVEL,status);
  1954. //
  1955. // Find the list of providers to call for this request.
  1956. //
  1957. indexArray = localArray;
  1958. status = MprFindCallOrder(
  1959. NULL,
  1960. &indexArray,
  1961. &numProviders,
  1962. NETWORK_TYPE);
  1963. if (status != WN_SUCCESS) {
  1964. SetLastError(status);
  1965. return status;
  1966. }
  1967. //
  1968. // Loop through the list of providers until one answers the request,
  1969. // or the list is exhausted.
  1970. //
  1971. for (i=0; i<numProviders; i++) {
  1972. //
  1973. // Call the appropriate providers API entry point
  1974. //
  1975. provider = GlobalProviderInfo + indexArray[i];
  1976. if (provider->GetConnection != NULL) {
  1977. fcnSupported = TRUE;
  1978. __try {
  1979. //**************************************
  1980. // Actual call to Provider.
  1981. //**************************************
  1982. status = provider->GetConnection(
  1983. (LPWSTR) lpLocalName,
  1984. lpRemoteName,
  1985. lpBufferSize);
  1986. }
  1987. __except(EXCEPTION_EXECUTE_HANDLER) {
  1988. status = GetExceptionCode();
  1989. if (status != EXCEPTION_ACCESS_VIOLATION) {
  1990. MPR_LOG(ERROR,"WNetGetConnection:Unexpected Exception 0x%lx\n",status);
  1991. }
  1992. status = WN_BAD_POINTER;
  1993. }
  1994. ////////////////////////////////////////////////////////////
  1995. // //
  1996. // The following code attempts to give the user the //
  1997. // most sensible error message. We have 3 clasess of //
  1998. // errors. On first class we stop routing. On second //
  1999. // continue but ignore that error (not significant //
  2000. // because the provider didnt think it was his). On //
  2001. // the last the provider returned an interesting (or //
  2002. // significant) error. We still route, but remember //
  2003. // it so it takes precedence over the non significant //
  2004. // ones. //
  2005. // //
  2006. ////////////////////////////////////////////////////////////
  2007. //
  2008. // Remember the first error, if it hasn't already been set
  2009. //
  2010. if (dwFirstError == WN_SUCCESS)
  2011. dwFirstError = status ;
  2012. //
  2013. // If the provider returns one of these errors, stop routing.
  2014. //
  2015. if ((status == WN_BAD_POINTER) ||
  2016. (status == WN_MORE_DATA) ||
  2017. (status == WN_SUCCESS))
  2018. {
  2019. //
  2020. // we either succeeded or have problems that means we
  2021. // should not continue (eg. bad input causing exception).
  2022. // and we make sure this is the error reported.
  2023. //
  2024. dwFirstError = status ;
  2025. dwFirstSignificantError = status ;
  2026. statusFlag = 0;
  2027. if ( lpProviderIndex != NULL ) {
  2028. *lpProviderIndex = indexArray[i];
  2029. }
  2030. break;
  2031. }
  2032. //
  2033. // If the provider returns one of these errors, continue
  2034. // trying other providers, but do not remember as a
  2035. // significant error, because the provider is probably not
  2036. // interested. StatusFlag is use to detect the case where
  2037. // a provider is not started.
  2038. //
  2039. else if (status == WN_NO_NETWORK)
  2040. {
  2041. statusFlag |= NO_NET;
  2042. }
  2043. else if ((status == WN_NOT_CONNECTED) ||
  2044. (status == WN_BAD_LOCALNAME))
  2045. {
  2046. //
  2047. // WN_NOT_CONNECTED means that lpLocalName is not a
  2048. // redirected device for this provider.
  2049. //
  2050. statusFlag |= BAD_NAME;
  2051. }
  2052. //
  2053. // If a provider returns one of these errors, we continue
  2054. // trying, but remember the error as a significant one. We
  2055. // report it to the user if it is the first. We do this so
  2056. // that if the first provider returns NotConnected and last
  2057. // returns BadPassword, we report the Password Error.
  2058. //
  2059. else
  2060. {
  2061. //
  2062. // All other errors are considered more significant
  2063. // than the ones above
  2064. //
  2065. if (!dwFirstSignificantError && status)
  2066. dwFirstSignificantError = status ;
  2067. statusFlag = OTHER_ERRS;
  2068. }
  2069. }
  2070. }
  2071. //
  2072. // If we failed, set final error, in order of importance.
  2073. // Significant errors take precedence. Otherwise we always
  2074. // report the first error.
  2075. //
  2076. if (status != WN_SUCCESS)
  2077. {
  2078. status = (dwFirstSignificantError != WN_SUCCESS) ?
  2079. dwFirstSignificantError :
  2080. dwFirstError ;
  2081. }
  2082. if (fcnSupported == FALSE) {
  2083. //
  2084. // No providers in the list support the API function. Therefore,
  2085. // we assume that no networks are installed.
  2086. //
  2087. status = WN_NOT_SUPPORTED;
  2088. }
  2089. //
  2090. // If memory was allocated by MprFindCallOrder, free it.
  2091. //
  2092. if (indexArray != localArray) {
  2093. LocalFree(indexArray);
  2094. }
  2095. //
  2096. // Handle special errors.
  2097. //
  2098. if (statusFlag == (NO_NET | BAD_NAME)) {
  2099. //
  2100. // Check to see if there was a mix of special errors that occured.
  2101. // If so, pass back the combined error message. Otherwise, let the
  2102. // last error returned get passed back.
  2103. //
  2104. status = WN_NO_NET_OR_BAD_PATH;
  2105. }
  2106. CheckRemembered:
  2107. //
  2108. // Handle normal errors passed back from the provider
  2109. //
  2110. if (status != WN_SUCCESS) {
  2111. if (status == WN_NOT_CONNECTED) {
  2112. //
  2113. // If not connected, but there is an entry for the LocalName
  2114. // in the registry, then return the remote name that was stored
  2115. // with it.
  2116. //
  2117. if (MprGetRemoteName(
  2118. (LPWSTR) lpLocalName,
  2119. lpBufferSize,
  2120. lpRemoteName,
  2121. &status)) {
  2122. if (status == WN_SUCCESS) {
  2123. status = WN_CONNECTION_CLOSED;
  2124. }
  2125. }
  2126. }
  2127. SetLastError(status);
  2128. }
  2129. return status;
  2130. }
  2131. DWORD
  2132. WNetGetConnection2W (
  2133. IN LPWSTR lpLocalName,
  2134. OUT LPVOID lpBuffer,
  2135. IN OUT LPDWORD lpBufferSize
  2136. )
  2137. /*++
  2138. Routine Description:
  2139. Just like WNetGetConnectionW except this one returns the provider name
  2140. that the device is attached through
  2141. Arguments:
  2142. lpBuffer will contain a WNET_CONNECTIONINFO structure
  2143. lpBufferSize is the number of bytes required for the buffer
  2144. Return Value:
  2145. --*/
  2146. {
  2147. DWORD status = WN_SUCCESS ;
  2148. DWORD iProvider = 0 ;
  2149. DWORD nBytesNeeded = 0 ;
  2150. DWORD cchBuff = 0 ;
  2151. DWORD nTotalSize = *lpBufferSize ;
  2152. LPTSTR lpRemoteName= (LPTSTR) ((BYTE*) lpBuffer +
  2153. sizeof(WNET_CONNECTIONINFO)) ;
  2154. WNET_CONNECTIONINFO * pconninfo = (WNET_CONNECTIONINFO *) lpBuffer ;
  2155. //
  2156. // If they didn't pass in a big enough buffer for even the structure,
  2157. // then make the size zero (so the buffer isn't accessed at all) and
  2158. // let the API figure out the buffer size
  2159. //
  2160. if ( *lpBufferSize < sizeof( WNET_CONNECTIONINFO) ) {
  2161. *lpBufferSize = 0 ;
  2162. cchBuff = 0 ;
  2163. }
  2164. else {
  2165. //
  2166. // MprGetConnection is expecting character counts, so convert
  2167. // after offsetting into the structure (places remote name directly
  2168. // in the structure).
  2169. //
  2170. cchBuff = (*lpBufferSize - sizeof(WNET_CONNECTIONINFO))/sizeof(TCHAR) ;
  2171. }
  2172. MprCheckProviders();
  2173. CProviderSharedLock PLock;
  2174. status = MprGetConnection(
  2175. lpLocalName,
  2176. lpRemoteName,
  2177. &cchBuff,
  2178. &iProvider);
  2179. if ( status == WN_SUCCESS ||
  2180. status == WN_CONNECTION_CLOSED ||
  2181. status == WN_MORE_DATA ) {
  2182. //
  2183. // Now we need to determine the buffer requirements for the
  2184. // structure and provider name
  2185. //
  2186. // (Note that if MprGetConnection returns WN_CONNECTION_CLOSED, it
  2187. // does not touch the value of iProvider. So iProvider will retain
  2188. // its somewhat arbitrary initial value of 0, meaning the first
  2189. // provider in the array.)
  2190. //
  2191. LPTSTR lpProvider = GlobalProviderInfo[iProvider].Resource.lpProvider;
  2192. //
  2193. // Calculate the required buffer size.
  2194. //
  2195. nBytesNeeded = sizeof( WNET_CONNECTIONINFO ) +
  2196. (STRLEN( lpProvider) + 1) * sizeof(TCHAR);
  2197. if ( status == WN_MORE_DATA )
  2198. {
  2199. nBytesNeeded += cchBuff * sizeof(TCHAR);
  2200. }
  2201. else
  2202. {
  2203. nBytesNeeded += (STRLEN( lpRemoteName) + 1) * sizeof(TCHAR);
  2204. }
  2205. if ( nTotalSize < nBytesNeeded ) {
  2206. status = WN_MORE_DATA;
  2207. *lpBufferSize = nBytesNeeded;
  2208. return status;
  2209. }
  2210. //
  2211. // Place the provider name in the buffer and initialize the
  2212. // structure to point to the strings.
  2213. //
  2214. pconninfo->lpRemoteName = lpRemoteName ;
  2215. pconninfo->lpProvider = STRCPY( (LPTSTR)
  2216. ((BYTE*) lpBuffer + sizeof(WNET_CONNECTIONINFO) +
  2217. (STRLEN( lpRemoteName ) + 1) * sizeof(TCHAR)),
  2218. lpProvider);
  2219. }
  2220. return status;
  2221. }
  2222. //===================================================================
  2223. // WNetGetConnection3W
  2224. //===================================================================
  2225. class CGetConnection3 : public CRoutedOperation
  2226. {
  2227. public:
  2228. CGetConnection3(
  2229. LPCWSTR lpLocalName,
  2230. LPCWSTR lpProviderName,
  2231. DWORD dwLevel,
  2232. LPVOID lpBuffer,
  2233. LPDWORD lpBufferSize
  2234. ) :
  2235. DBGPARM(CRoutedOperation("GetConnection3"))
  2236. _lpLocalName (lpLocalName ),
  2237. _lpProviderName(lpProviderName),
  2238. _dwLevel (dwLevel ),
  2239. _lpBuffer (lpBuffer ),
  2240. _lpBufferSize (lpBufferSize )
  2241. { }
  2242. private:
  2243. LPCWSTR _lpLocalName;
  2244. LPCWSTR _lpProviderName;
  2245. DWORD _dwLevel;
  2246. LPVOID _lpBuffer;
  2247. LPDWORD _lpBufferSize;
  2248. DECLARE_CROUTED
  2249. };
  2250. DWORD
  2251. CGetConnection3::ValidateRoutedParameters(
  2252. LPCWSTR * ppProviderName,
  2253. LPCWSTR * ppRemoteName,
  2254. LPCWSTR * ppLocalName
  2255. )
  2256. {
  2257. if (_dwLevel != WNGC_INFOLEVEL_DISCONNECTED)
  2258. {
  2259. return WN_BAD_LEVEL;
  2260. }
  2261. if (MprDeviceType(_lpLocalName) != REDIR_DEVICE)
  2262. {
  2263. return WN_BAD_LOCALNAME;
  2264. }
  2265. if (IS_BAD_BYTE_BUFFER(_lpBuffer, _lpBufferSize))
  2266. {
  2267. return WN_BAD_POINTER;
  2268. }
  2269. //
  2270. // Set parameters used by base class. Note that we set *ppLocalName
  2271. // to NULL to prevent the base class from checking it for "localness"
  2272. // since this is a private API called by the shell for network drives
  2273. // only. If we ever want to check the local name, the line below will
  2274. // have to be changed to:
  2275. //
  2276. // *ppLocalName = _lpLocalName;
  2277. //
  2278. *ppProviderName = _lpProviderName;
  2279. *ppLocalName = NULL;
  2280. return WN_SUCCESS;
  2281. }
  2282. DWORD
  2283. CGetConnection3::TestProvider(
  2284. const PROVIDER * pProvider
  2285. )
  2286. {
  2287. ASSERT_INITIALIZED(NETWORK);
  2288. if (pProvider->GetConnection3 != NULL)
  2289. {
  2290. return ( pProvider->GetConnection3(
  2291. _lpLocalName,
  2292. _dwLevel,
  2293. _lpBuffer,
  2294. _lpBufferSize) );
  2295. }
  2296. else if (pProvider->GetConnection == NULL)
  2297. {
  2298. return WN_NOT_SUPPORTED;
  2299. }
  2300. else
  2301. {
  2302. // Just verify that the provider owns the connection, and if so,
  2303. // assume that it's not disconnected
  2304. WCHAR wszRemoteName[40];
  2305. DWORD nLength = LENGTH(wszRemoteName);
  2306. DWORD status = pProvider->GetConnection(
  2307. (LPWSTR)_lpLocalName,
  2308. wszRemoteName,
  2309. &nLength);
  2310. if (status == WN_SUCCESS || status == WN_MORE_DATA)
  2311. {
  2312. // The provider owns the connection
  2313. if (*_lpBufferSize < sizeof(WNGC_CONNECTION_STATE))
  2314. {
  2315. *_lpBufferSize = sizeof(WNGC_CONNECTION_STATE);
  2316. status = WN_MORE_DATA;
  2317. }
  2318. else
  2319. {
  2320. ((LPWNGC_CONNECTION_STATE)_lpBuffer)->dwState =
  2321. WNGC_CONNECTED;
  2322. status = WN_SUCCESS;
  2323. }
  2324. }
  2325. return status;
  2326. }
  2327. }
  2328. DWORD APIENTRY
  2329. WNetGetConnection3W(
  2330. IN LPCWSTR lpLocalName,
  2331. IN LPCWSTR lpProviderName OPTIONAL,
  2332. IN DWORD dwLevel,
  2333. OUT LPVOID lpBuffer,
  2334. IN OUT LPDWORD lpBufferSize
  2335. )
  2336. /*++
  2337. Routine Description:
  2338. This function returns miscellaneous information about a network
  2339. connection, as specified by the info level parameter.
  2340. Arguments:
  2341. lpLocalName - The name of a redirected local device for which
  2342. information is required.
  2343. lpProviderName - The name of the provider responsible for the connection,
  2344. if known.
  2345. dwLevel - Level of information required. Supported levels are:
  2346. 1 - Determine whether the connection is currently disconnected.
  2347. lpBuffer - Buffer in which to return the information if the call is
  2348. successful. The format of the information returned is as follows,
  2349. depending on dwLevel:
  2350. Level 1 - A DWORD is returned whose value is one of the following:
  2351. WNGETCON_CONNECTED
  2352. WNGETCON_DISCONNECTED
  2353. lpBufferSize - On input, size of the buffer in bytes. If the buffer
  2354. is too small, the required size will be written here.
  2355. For level 1 the required size is sizeof(DWORD).
  2356. Return Value:
  2357. WN_SUCCESS - successful.
  2358. WN_MORE_DATA - buffer is too small.
  2359. WN_BAD_LOCALNAME - lpLocalName is not a valid device name.
  2360. WN_BAD_PROVIDER - lpProviderName is not a recognized provider name.
  2361. WN_NOT_CONNECTED - the device specified by lpLocalName is not redirected.
  2362. WN_CONNECTION_CLOSED - the device specified by lpLocalName is not
  2363. redirected, but is a remembered (unavailable) connection.
  2364. --*/
  2365. {
  2366. CGetConnection3 GetConnection3(lpLocalName,
  2367. lpProviderName,
  2368. dwLevel,
  2369. lpBuffer,
  2370. lpBufferSize);
  2371. DWORD status = GetConnection3.Perform(TRUE);
  2372. if (status == WN_NOT_CONNECTED &&
  2373. MprFindDriveInRegistry((LPWSTR)lpLocalName,NULL))
  2374. {
  2375. status = WN_CONNECTION_CLOSED;
  2376. }
  2377. return status;
  2378. }
  2379. DWORD
  2380. WNetRestoreConnectionW(
  2381. IN HWND hWnd,
  2382. IN LPCWSTR lpDevice
  2383. )
  2384. {
  2385. return WNetRestoreConnection2W(hWnd, lpDevice, 0, NULL);
  2386. }
  2387. DWORD
  2388. WNetRestoreConnection2W(
  2389. IN HWND hWnd,
  2390. IN LPCWSTR lpDevice,
  2391. IN DWORD dwFlags,
  2392. OUT BOOL* pfReconnectFailed
  2393. )
  2394. /*++
  2395. Routine Description:
  2396. This function create another thread which does the connection.
  2397. In the main thread it create a dialog window to monitor the state of
  2398. the connection.
  2399. Arguments:
  2400. hwnd - This is a window handle that may be used as owner of any
  2401. dialog brought up by MPR (eg. password prompt).
  2402. lpDevice - This may be NULL or may contain a device name such as
  2403. "x:". If NULL, all remembered connections are restored. Otherwise,
  2404. the remembered connection for the specified device, if any are
  2405. restored.
  2406. dwFlags - WNRC_NOUI specifies that no UI should be shown. Connections that
  2407. fail to restore will be reattempted at next logon.
  2408. Return Value:
  2409. --*/
  2410. {
  2411. DWORD status = WN_SUCCESS;
  2412. DWORD print_connect_status = WN_SUCCESS;
  2413. DWORD numSubKeys;
  2414. DWORD RegMaxWait = 0;
  2415. HANDLE hThread = NULL;
  2416. HANDLE ThreadID;
  2417. LPCONNECTION_INFO ConnectArray;
  2418. PARAMETERS *lpParams = NULL;
  2419. BOOL DontDefer = FALSE;
  2420. LONG fDoCleanup = FALSE;
  2421. if (pfReconnectFailed)
  2422. {
  2423. *pfReconnectFailed = FALSE;
  2424. }
  2425. //
  2426. // Check the registry to see if we need to restore connections or not,
  2427. // and whether we can defer them.
  2428. // This is done only if lpDevice is NULL (restoring all).
  2429. // (If a particular device is specified, it is always restored undeferred.)
  2430. //
  2431. // Interpretation of the RestoreConnection value is:
  2432. // 0 - don't restore connections (and ignore the DeferConnection value)
  2433. // default - restore connections
  2434. //
  2435. // Interpretation of the DeferConnection value is:
  2436. // 0 - don't defer any connections
  2437. // default - defer every connection that can be deferred
  2438. //
  2439. if ( lpDevice == NULL )
  2440. {
  2441. HKEY providerKeyHandle;
  2442. DWORD ValueType;
  2443. DWORD fRestoreConnection = TRUE;
  2444. DWORD Temp = sizeof( fRestoreConnection );
  2445. if( MprOpenKey( HKEY_LOCAL_MACHINE, // hKey
  2446. NET_PROVIDER_KEY, // lpSubKey
  2447. &providerKeyHandle, // Newly Opened Key Handle
  2448. DA_READ)) // Desired Access
  2449. {
  2450. if ( RegQueryValueEx(
  2451. providerKeyHandle,
  2452. RESTORE_CONNECTION_VALUE,
  2453. NULL,
  2454. &ValueType, // not used
  2455. (LPBYTE) &fRestoreConnection,
  2456. &Temp) == NO_ERROR )
  2457. {
  2458. if ( !fRestoreConnection )
  2459. {
  2460. MPR_LOG0(RESTORE, "Registry says NOT to restore connections\n");
  2461. RegCloseKey( providerKeyHandle );
  2462. return WN_SUCCESS;
  2463. }
  2464. }
  2465. DWORD fDeferConnection;
  2466. if (MprGetKeyDwordValue(
  2467. providerKeyHandle,
  2468. DEFER_CONNECTION_VALUE,
  2469. &fDeferConnection) &&
  2470. fDeferConnection == 0 )
  2471. {
  2472. MPR_LOG0(RESTORE, "Registry says NOT to defer restored connections\n");
  2473. DontDefer = TRUE;
  2474. }
  2475. RegCloseKey( providerKeyHandle );
  2476. }
  2477. }
  2478. __try {
  2479. if (lpDevice != NULL)
  2480. {
  2481. if (MprDeviceType (lpDevice) != REDIR_DEVICE)
  2482. {
  2483. status = WN_BAD_LOCALNAME;
  2484. }
  2485. }
  2486. }
  2487. __except (EXCEPTION_EXECUTE_HANDLER)
  2488. {
  2489. status = GetExceptionCode();
  2490. if (status != EXCEPTION_ACCESS_VIOLATION)
  2491. {
  2492. MPR_LOG (ERROR, "WNetRestoreConnectionW:Unexpected Exception 0x%1x\n",status);
  2493. }
  2494. status = WN_BAD_POINTER;
  2495. }
  2496. if (status != WN_SUCCESS)
  2497. {
  2498. SetLastError(status);
  2499. return status;
  2500. }
  2501. //
  2502. // MprCreateConnectionArray may use the providers
  2503. //
  2504. MprCheckProviders();
  2505. {
  2506. CProviderSharedLock PLock;
  2507. //
  2508. // Read all the connection info from the registry.
  2509. //
  2510. status = MprCreateConnectionArray (&numSubKeys,
  2511. lpDevice,
  2512. &RegMaxWait,
  2513. &ConnectArray);
  2514. if (lpDevice == NULL)
  2515. {
  2516. //
  2517. // only wory about Print if restoring all
  2518. //
  2519. print_connect_status = MprAddPrintersToConnArray (&numSubKeys,
  2520. &ConnectArray);
  2521. }
  2522. //
  2523. // if both failed, report first error. else do the best we can.
  2524. //
  2525. if (status != WN_SUCCESS && print_connect_status != WN_SUCCESS)
  2526. {
  2527. SetLastError (status);
  2528. return status;
  2529. }
  2530. if (numSubKeys == 0)
  2531. {
  2532. return(WN_SUCCESS);
  2533. }
  2534. INIT_IF_NECESSARY(NETWORK_LEVEL,status);
  2535. //
  2536. // If there are no providers, return NO_NETWORK
  2537. //
  2538. if (GlobalNumActiveProviders == 0) {
  2539. SetLastError(WN_NO_NETWORK);
  2540. return(WN_NO_NETWORK);
  2541. }
  2542. //
  2543. // Refcount all the provider DLLs we may use since we may
  2544. // have to call DoProfileErrorDialog, which calls outside
  2545. // mpr.dll and can potentially loop back, causing deadlock.
  2546. // By refcounting here, we don't have to worry about
  2547. // releasing/reacquiring the lock or over-refcounting the
  2548. // provider DLLs later on.
  2549. //
  2550. MprRefcountConnectionArray(ConnectArray, numSubKeys);
  2551. }
  2552. // If lpDevice is not NULL, call MprRestoreThisConnection directly.
  2553. if (lpDevice)
  2554. {
  2555. status = MprRestoreThisConnection (hWnd, NULL, &ConnectArray[0], dwFlags);
  2556. ConnectArray[0].Status = status;
  2557. if ((status != WN_SUCCESS) &&
  2558. (status != WN_CANCEL) &&
  2559. (status != WN_CONTINUE))
  2560. {
  2561. if (!(dwFlags & WNRC_NOUI))
  2562. {
  2563. DoProfileErrorDialog (hWnd,
  2564. ConnectArray[0].NetResource.lpLocalName,
  2565. ConnectArray[0].NetResource.lpRemoteName,
  2566. ConnectArray[0].NetResource.lpProvider,
  2567. ConnectArray[0].Status,
  2568. FALSE, //No cancel button.
  2569. NULL,
  2570. NULL,
  2571. NULL); // no skip future errors checkbox
  2572. }
  2573. if (pfReconnectFailed)
  2574. {
  2575. *pfReconnectFailed = TRUE;
  2576. }
  2577. }
  2578. }
  2579. else do // not a loop. error break out.
  2580. {
  2581. //
  2582. // Initialize lpParams.
  2583. //
  2584. lpParams = (PARAMETERS *) LocalAlloc (LPTR,
  2585. sizeof (PARAMETERS));
  2586. if ((lpParams == NULL) ||
  2587. (lpParams->hDlgCreated
  2588. = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL ||
  2589. (lpParams->hDlgFailed
  2590. = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL ||
  2591. (lpParams->hDonePassword
  2592. = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL)
  2593. {
  2594. status = GetLastError();
  2595. if (lpParams != NULL)
  2596. {
  2597. if (lpParams->hDlgCreated != NULL)
  2598. CloseHandle(lpParams->hDlgCreated);
  2599. if (lpParams->hDlgFailed != NULL)
  2600. CloseHandle(lpParams->hDlgFailed);
  2601. if (lpParams->hDonePassword != NULL)
  2602. CloseHandle(lpParams->hDonePassword);
  2603. }
  2604. break;
  2605. }
  2606. lpParams->numSubKeys = numSubKeys;
  2607. lpParams->RegMaxWait = RegMaxWait;
  2608. lpParams->ConnectArray = ConnectArray;
  2609. lpParams->dwRestoreFlags = dwFlags;
  2610. lpParams->fReconnectFailed = FALSE;
  2611. //
  2612. // Decide whether to show the "Restoring Connections" dialog.
  2613. // In general, if a connection will be made as a user other than the
  2614. // default logged-on user, we need to give the user a chance to enter
  2615. // the password and have it validated, hence we need to contact the
  2616. // server, so we should show the dialog.
  2617. //
  2618. BOOL NeedDialog;
  2619. if (DontDefer)
  2620. {
  2621. NeedDialog = TRUE;
  2622. }
  2623. else
  2624. {
  2625. NeedDialog = FALSE;
  2626. //
  2627. // Get the default user and domain names to connect as
  2628. //
  2629. PUNICODE_STRING UserName;
  2630. PUNICODE_STRING DomainName;
  2631. NTSTATUS ntStatus = LsaGetUserName(&UserName, &DomainName);
  2632. if (NT_SUCCESS(ntStatus))
  2633. {
  2634. MPR_LOG2(RESTORE,"Default domain name = \"%ws\", user name = \"%ws\"\n",
  2635. DomainName->Buffer, UserName->Buffer);
  2636. }
  2637. else
  2638. {
  2639. MPR_LOG(ERROR, "LsaGetUserName failed, %#lx\n", ntStatus);
  2640. DomainName = NULL;
  2641. UserName = NULL;
  2642. }
  2643. //
  2644. // If the logon domain is the local machine, don't defer connections.
  2645. // This is a NT 4.0 workaround for the most common case of bug 36827.
  2646. // When connecting to an LM server, if the user name happens to
  2647. // match a local user name on the target server, the server will
  2648. // normally attempt to log on using THAT user's account; hence we
  2649. // need to prompt for the password. We need to do this since this
  2650. // behavior is by design in the redirector.
  2651. //
  2652. WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH+1];
  2653. DWORD nSize = LENGTH(ComputerName);
  2654. if (DomainName == NULL ||
  2655. GetComputerName(ComputerName, &nSize) == FALSE ||
  2656. _wcsicmp(ComputerName, DomainName->Buffer) == 0)
  2657. {
  2658. MPR_LOG0(RESTORE, "Local logon, will not defer connections\n");
  2659. NeedDialog = TRUE;
  2660. }
  2661. else
  2662. {
  2663. //
  2664. // Prescan the connections to determine which ones we can
  2665. // defer and whether we need the reconnect dialog. If all
  2666. // connections are deferred, we do not bother with the dialog.
  2667. //
  2668. for (DWORD i = 0; i < numSubKeys; i++)
  2669. {
  2670. if (! ConnectArray[i].ContinueFlag)
  2671. {
  2672. continue;
  2673. }
  2674. //
  2675. // The DEFER_UNKNOWN flag means don't defer the connection
  2676. // on this logon, but try the default credentials and see
  2677. // if they work; if so, the connection can be deferred at
  2678. // subsequent logons.
  2679. //
  2680. // Don't defer the connection if a password was explicitly
  2681. // specified when the connection was made. This covers
  2682. // cases in which the redir won't send the default password
  2683. // to the server at connect time because the server doesn't
  2684. // support encrypted passwords.
  2685. //
  2686. if (! (ConnectArray[i].DeferFlags & DEFER_UNKNOWN)
  2687. &&
  2688. ! (ConnectArray[i].DeferFlags & DEFER_EXPLICIT_PASSWORD)
  2689. &&
  2690. MprUserNameMatch(DomainName,
  2691. UserName,
  2692. ConnectArray[i].UserName,
  2693. FALSE))
  2694. {
  2695. //
  2696. // If the user name is the default user name, we can safely
  2697. // replace it with a NULL. (This is not only more optimal, but
  2698. // also required in order to work around a LM redir problem
  2699. // with the way credentials for deferred connections are stored.)
  2700. //
  2701. LocalFree(ConnectArray[i].UserName);
  2702. ConnectArray[i].UserName = NULL;
  2703. //
  2704. // It's OK to defer the connection iff the remembered user
  2705. // name matches the default user name and the provider
  2706. // supports deferred connections.
  2707. //
  2708. if ((ConnectArray[i].dwConnectCaps & WNNC_CON_DEFER)
  2709. &&
  2710. (ConnectArray[i].pfAddConnection3 != NULL))
  2711. {
  2712. //
  2713. // Defer was initialized to 0 when the array was
  2714. // allocated.
  2715. // Note that we don't defer if an lpDevice was supplied.
  2716. //
  2717. ConnectArray[i].Defer = TRUE;
  2718. }
  2719. }
  2720. if (! ConnectArray[i].Defer)
  2721. {
  2722. NeedDialog = TRUE;
  2723. }
  2724. } // for each connection
  2725. MprSortConnectionArray(ConnectArray, numSubKeys);
  2726. }
  2727. if (DomainName != NULL)
  2728. {
  2729. LsaFreeMemory(DomainName->Buffer);
  2730. LsaFreeMemory(DomainName);
  2731. }
  2732. if (UserName != NULL)
  2733. {
  2734. LsaFreeMemory(UserName->Buffer);
  2735. LsaFreeMemory(UserName);
  2736. }
  2737. }
  2738. //
  2739. // If we are:
  2740. // USING DIALOGS WHEN RESTORING CONNECTIONS...
  2741. //
  2742. // This main thread will used to service a windows event loop
  2743. // by calling ShowReconnectDialog. Therefore, a new thread
  2744. // must be created to actually restore the connections. As we
  2745. // attempt to restore each connection, a message is posted in
  2746. // this event loop that causes it to put up a dialog that
  2747. // describes the connection and has a "cancel" option.
  2748. //
  2749. if (! NeedDialog)
  2750. {
  2751. lpParams->hDlg = INVALID_WINDOW_HANDLE;
  2752. // CODEWORK: We are using this INVALID_WINDOW_HANDLE to tell
  2753. // the various routines whether there are 2 threads or not.
  2754. // Instead we should add a new field, BOOL fSeparateThread,
  2755. // to PARAMETERS. This requires changing routines in mprui.dll
  2756. // as well as mpr.dll.
  2757. DoRestoreConnection(lpParams);
  2758. }
  2759. else
  2760. {
  2761. HANDLE hThreadToken;
  2762. // If the main thread is impersonating, we have to copy its token to
  2763. // the new thread that is being spawned so it runs in the correct context
  2764. if (!OpenThreadToken (GetCurrentThread(),
  2765. TOKEN_IMPERSONATE,
  2766. TRUE,
  2767. &hThreadToken)
  2768. &&
  2769. !OpenProcessToken(GetCurrentProcess(),
  2770. TOKEN_IMPERSONATE,
  2771. &hThreadToken))
  2772. {
  2773. //
  2774. // Couldn't open a token on the thread or the process
  2775. //
  2776. status = GetLastError();
  2777. }
  2778. else
  2779. {
  2780. // lpParams->hDlg was initialized to 0 by LocalAlloc
  2781. // and will be set to an HWND by ShowReconnectDialog
  2782. hThread = CreateThread (NULL,
  2783. 0,
  2784. (LPTHREAD_START_ROUTINE) &DoRestoreConnection,
  2785. (LPVOID) lpParams,
  2786. CREATE_SUSPENDED,
  2787. (LPDWORD) &ThreadID);
  2788. if (hThread == NULL)
  2789. {
  2790. status = GetLastError();
  2791. }
  2792. else
  2793. {
  2794. //
  2795. // Make sure the worker thread isn't killed if
  2796. // this thread finishes while it's wedged in a
  2797. // provider call (in MprRestoreThisConnection).
  2798. // The DLL is unloaded by DoRestoreConnection
  2799. //
  2800. lpParams->hDll = LoadLibraryEx( L"mpr.dll",
  2801. NULL,
  2802. LOAD_WITH_ALTERED_SEARCH_PATH );
  2803. if (lpParams->hDll == NULL) {
  2804. MPR_LOG1(RESTORE,
  2805. "LoadLibraryEx for mpr.dll FAILED %d\n",
  2806. GetLastError());
  2807. //
  2808. // This shouldn't happen since all we're
  2809. // doing is upping our refcount
  2810. //
  2811. ASSERT(FALSE);
  2812. }
  2813. //
  2814. // Assign the impersonation token to the
  2815. // thread and resume/start it
  2816. //
  2817. if (!SetThreadToken(&hThread, hThreadToken))
  2818. {
  2819. status = GetLastError();
  2820. TerminateThread(hThread, NO_ERROR);
  2821. CloseHandle(hThread);
  2822. }
  2823. else
  2824. {
  2825. ResumeThread(hThread);
  2826. CloseHandle(hThread);
  2827. status = ShowReconnectDialog(hWnd, lpParams);
  2828. }
  2829. if (status == WN_SUCCESS && lpParams->status != WN_CANCEL &&
  2830. lpParams->status != WN_SUCCESS )
  2831. {
  2832. SetLastError (lpParams->status);
  2833. }
  2834. if (lpParams->status != WN_CANCEL)
  2835. {
  2836. status = MprNotifyErrors (hWnd, ConnectArray, numSubKeys, dwFlags);
  2837. }
  2838. }
  2839. CloseHandle(hThreadToken);
  2840. }
  2841. }
  2842. //
  2843. // Only if we make it to the end of the do-while loop do the thread
  2844. // parameters have to be cleaned up. Since either this thread or
  2845. // the worker thread might exit first (if the user hits Cancel and
  2846. // the worker thread is stuck in a provider call, this thread could
  2847. // exit first), the InterlockedExchange calls here and at the end
  2848. // of DoRestoreConnection ensure that the last thread leaving
  2849. // does the cleanup.
  2850. //
  2851. // NOTE: This relies on the fact that the only break out of
  2852. // this do-while occurs when LocalAlloc for lpParams FAILS.
  2853. // Adding new break statements may break this logic!
  2854. //
  2855. //
  2856. // Case 1: lpParams never gets allocated -- this bit of code
  2857. // never gets hit since we break out of the loop above
  2858. // and nobody cleans up (correctly)
  2859. //
  2860. // Case 2: CreateThread fails -- hThread is NULL, so this thread
  2861. // needs to clean up
  2862. //
  2863. // (CODEWORK -- should this thread call DoRestoreConnection
  2864. // directly in that case?)
  2865. //
  2866. // Case 3: The worker thread exits first -- it changes fDoCleanup
  2867. // to TRUE before it exits and this thread cleans up
  2868. //
  2869. // Case 4: This thread exits first (user cancels dialog) -- this
  2870. // thread changes fDoCleanup to TRUE and orphans the
  2871. // worker thread. When it finishes its provider call,
  2872. // it reads fDoCleanup and cleans up.
  2873. //
  2874. // Poll the worker thread's data to see if reconnect failed.
  2875. if (pfReconnectFailed)
  2876. {
  2877. *pfReconnectFailed = lpParams->fReconnectFailed;
  2878. }
  2879. fDoCleanup = (InterlockedExchange(&lpParams->fDoCleanup, TRUE)
  2880. ||
  2881. hThread == NULL);
  2882. } while (FALSE);
  2883. //
  2884. // Is this thread supposed to clean up?
  2885. //
  2886. if (fDoCleanup)
  2887. {
  2888. MPR_LOG0(RESTORE, "Main thread will perform cleanup\n");
  2889. ASSERT(lpParams != NULL);
  2890. //
  2891. // Free up resources in preparation to return.
  2892. //
  2893. if (!CloseHandle(lpParams->hDlgCreated))
  2894. status = GetLastError();
  2895. if (!CloseHandle(lpParams->hDlgFailed))
  2896. status = GetLastError();
  2897. if (!CloseHandle(lpParams->hDonePassword))
  2898. status = GetLastError();
  2899. LocalFree(lpParams);
  2900. MprFreeConnectionArray(ConnectArray, numSubKeys);
  2901. }
  2902. // Send a notification about the new network drive(s).
  2903. if( (g_LUIDDeviceMapsEnabled == TRUE) && (lpDevice != NULL))
  2904. {
  2905. // Use LUID broadcast mechanism
  2906. MprBroadcastDriveChange(
  2907. lpDevice,
  2908. FALSE ); // not a Delete Message
  2909. }
  2910. else
  2911. {
  2912. MprNotifyShell(L" :");
  2913. }
  2914. if (status != WN_SUCCESS)
  2915. {
  2916. SetLastError(status);
  2917. }
  2918. return status;
  2919. }
  2920. VOID
  2921. MprSortConnectionArray(
  2922. LPCONNECTION_INFO lpcConnectArray,
  2923. DWORD dwNumSubKeys
  2924. )
  2925. /*++
  2926. Routine Description:
  2927. This function sorts the array of connections by placing the
  2928. connections that can be deferred before those that can't
  2929. Arguments:
  2930. lpcConnectArray -- The array of CONNECTION_INFO structures
  2931. dwNumSubKeys -- The number of structures in the array
  2932. Notes:
  2933. This sort is done to improve behavior at boot. Deferred connections are
  2934. faster and less error-prone than non-deferred connections and errors can
  2935. cause popups that allow the user to stop reconnecting drives, which leaves
  2936. the remaining drives in the annoying "Unavailable" state. Note that most
  2937. popups for drive reconnection at boot are for credentials, which is a popup
  2938. associated only with non-deferred connection. By reconnecting deferred
  2939. connections first, we avoid this problem.
  2940. --*/
  2941. {
  2942. INT nLow = 0;
  2943. INT nHigh = dwNumSubKeys - 1;
  2944. BOOL fSwap;
  2945. CONNECTION_INFO ciTemp;
  2946. do {
  2947. //
  2948. // Find the first non-deferred connection
  2949. //
  2950. while (nLow < (INT)dwNumSubKeys
  2951. &&
  2952. lpcConnectArray[nLow].Defer) {
  2953. nLow++;
  2954. }
  2955. //
  2956. // Find the last deferred connection
  2957. //
  2958. while (nHigh >= 0
  2959. &&
  2960. !lpcConnectArray[nHigh].Defer) {
  2961. nHigh--;
  2962. }
  2963. fSwap = (nLow < nHigh);
  2964. //
  2965. // Only swap if the pointers haven't crossed
  2966. // (otherwise we'd be undoing the sorting)
  2967. //
  2968. if (fSwap) {
  2969. ciTemp = lpcConnectArray[nLow];
  2970. lpcConnectArray[nLow++] = lpcConnectArray[nHigh];
  2971. lpcConnectArray[nHigh--] = ciTemp;
  2972. }
  2973. }
  2974. while (fSwap);
  2975. #if DBG
  2976. MPR_LOG0(RESTORE, "Order of sorted connection array is as follows:\n");
  2977. for (UINT i = 0; i < dwNumSubKeys; i++) {
  2978. MPR_LOG3(RESTORE,
  2979. "\tLocal: %ws, \tRemote: %ws, \tDefer: %d\n",
  2980. lpcConnectArray[i].NetResource.lpLocalName,
  2981. lpcConnectArray[i].NetResource.lpRemoteName,
  2982. lpcConnectArray[i].Defer);
  2983. }
  2984. #endif // DBG
  2985. }
  2986. VOID
  2987. MprRefcountConnectionArray(
  2988. LPCONNECTION_INFO lpcConnectArray,
  2989. DWORD dwNumSubkeys
  2990. )
  2991. /*++
  2992. Routine Description:
  2993. This function sorts refcounts the provider DLLs in the array
  2994. of connections in preparation for a call outside of mpr.dll
  2995. (which requires releasing the provider lock to avoid the
  2996. possibility of reentrancy and deadlock)
  2997. Arguments:
  2998. lpcConnectArray -- The array of CONNECTION_INFO structures
  2999. dwNumSubKeys -- The number of structures in the array
  3000. Notes:
  3001. --*/
  3002. {
  3003. UINT i;
  3004. LPPROVIDER lpProvider;
  3005. ASSERT_INITIALIZED(NETWORK);
  3006. for (i = 0; i < dwNumSubkeys; i++)
  3007. {
  3008. //
  3009. // If this hits, we're over-refcounting
  3010. //
  3011. ASSERT(lpcConnectArray[i].hProviderDll == NULL);
  3012. lpProvider = &GlobalProviderInfo[lpcConnectArray[i].ProviderIndex];
  3013. lpcConnectArray[i].hProviderDll = LoadLibraryEx(lpProvider->DllName,
  3014. NULL,
  3015. LOAD_WITH_ALTERED_SEARCH_PATH);
  3016. if (lpcConnectArray[i].hProviderDll != NULL)
  3017. {
  3018. lpcConnectArray[i].pfGetCaps = lpProvider->GetCaps;
  3019. lpcConnectArray[i].pfAddConnection3 = lpProvider->AddConnection3;
  3020. lpcConnectArray[i].pfAddConnection = lpProvider->AddConnection;
  3021. lpcConnectArray[i].pfCancelConnection = lpProvider->CancelConnection;
  3022. lpcConnectArray[i].dwConnectCaps = lpProvider->ConnectCaps;
  3023. }
  3024. else
  3025. {
  3026. MPR_LOG2(ERROR,
  3027. "MprRefcountConnectionArray: LoadLibraryEx on %ws failed %d\n",
  3028. lpProvider->DllName,
  3029. GetLastError());
  3030. }
  3031. }
  3032. }
  3033. DWORD
  3034. WNetSetConnectionW(
  3035. IN LPCWSTR lpName,
  3036. IN DWORD dwProperty,
  3037. IN LPVOID pvValue
  3038. )
  3039. /*++
  3040. Routine Description:
  3041. This function changes the characteristics of a network connection.
  3042. Arguments:
  3043. lpName - The name of either the redirected local device or a remote
  3044. network resource.
  3045. dwProperty - Identifies the property to be changed.
  3046. Current properties supported:
  3047. NETPROPERTY_PERSISTENT - pvValue points to a DWORD. If TRUE,
  3048. the connection is made persistent. If FALSE, the connection
  3049. is made non-persistent.
  3050. pvValue - Pointer to the property value. Type depends on dwProperty.
  3051. Return Value:
  3052. WN_SUCCESS - successful
  3053. WN_BAD_LOCALNAME or WN_NOT_CONNECTED - lpName is not a redirected device
  3054. --*/
  3055. {
  3056. DWORD status = WN_SUCCESS;
  3057. if (!(ARGUMENT_PRESENT(lpName) &&
  3058. ARGUMENT_PRESENT(pvValue)))
  3059. {
  3060. SetLastError(WN_BAD_POINTER);
  3061. return(WN_BAD_POINTER);
  3062. }
  3063. // NPGetUser and some Mpr internal functions use lpName as a non-const
  3064. // argument, so we have to make a copy
  3065. WCHAR * lpNameCopy = (WCHAR *) LocalAlloc(LMEM_FIXED, WCSSIZE(lpName));
  3066. if (lpNameCopy == NULL)
  3067. {
  3068. SetLastError(WN_OUT_OF_MEMORY);
  3069. return(WN_OUT_OF_MEMORY);
  3070. }
  3071. wcscpy(lpNameCopy, lpName);
  3072. switch (dwProperty)
  3073. {
  3074. case NETPROPERTY_PERSISTENT:
  3075. {
  3076. MprCheckProviders();
  3077. CProviderSharedLock PLock;
  3078. __try
  3079. {
  3080. //
  3081. // Value should be a boolean DWORD telling whether to
  3082. // remember or forget the connection
  3083. //
  3084. BOOL bRemember = * ((DWORD *) pvValue);
  3085. //
  3086. // Verify that lpName is a redirected local device, and
  3087. // identify the provider responsible
  3088. //
  3089. WCHAR wszRemoteName[MAX_PATH+1];
  3090. DWORD cbBuffer = sizeof(wszRemoteName);
  3091. DWORD iProvider; // provider index
  3092. status = MprGetConnection(
  3093. lpName,
  3094. wszRemoteName,
  3095. &cbBuffer,
  3096. &iProvider);
  3097. if (status == WN_CONNECTION_CLOSED)
  3098. {
  3099. //
  3100. // It's already a remembered connection. Nothing to do.
  3101. //
  3102. status = WN_SUCCESS;
  3103. __leave;
  3104. }
  3105. if (status != WN_SUCCESS)
  3106. {
  3107. ASSERT(status != WN_MORE_DATA);
  3108. __leave;
  3109. }
  3110. if (bRemember)
  3111. {
  3112. //
  3113. // Get the username for the connection from the provider
  3114. //
  3115. WCHAR wszUser[MAX_PATH+1];
  3116. cbBuffer = LENGTH(wszUser);
  3117. status = GlobalProviderInfo[iProvider].GetUser(
  3118. lpNameCopy, wszUser, &cbBuffer);
  3119. if (status != WN_SUCCESS)
  3120. {
  3121. ASSERT(status != WN_MORE_DATA);
  3122. __leave;
  3123. }
  3124. //
  3125. // Get the provider flags, if any
  3126. //
  3127. ASSERT_INITIALIZED(NETWORK);
  3128. BYTE ProviderFlags = 0;
  3129. if (GlobalProviderInfo[iProvider].GetReconnectFlags != NULL)
  3130. {
  3131. // This is an internal entry point so we don't bother with
  3132. // try-except
  3133. DWORD status2 = GlobalProviderInfo[iProvider].GetReconnectFlags(
  3134. lpNameCopy,
  3135. &ProviderFlags
  3136. );
  3137. if (status2 != WN_SUCCESS)
  3138. {
  3139. ProviderFlags = 0;
  3140. }
  3141. MPR_LOG3(RESTORE, "%ws wants flags %#x saved for %ws\n",
  3142. GlobalProviderInfo[iProvider].Resource.lpProvider,
  3143. ProviderFlags,
  3144. lpNameCopy);
  3145. }
  3146. //
  3147. // Make the connection persistent
  3148. //
  3149. status = I_MprSaveConn(
  3150. HKEY_CURRENT_USER,
  3151. GlobalProviderInfo[iProvider].Resource.lpProvider,
  3152. GlobalProviderInfo[iProvider].Type,
  3153. wszUser,
  3154. lpNameCopy,
  3155. wszRemoteName,
  3156. (wcslen(lpName) == 2 && lpName[1] == L':')
  3157. ? RESOURCETYPE_DISK
  3158. : RESOURCETYPE_PRINT,
  3159. ProviderFlags,
  3160. DEFER_UNKNOWN
  3161. );
  3162. }
  3163. else
  3164. {
  3165. //
  3166. // Make the connection non-persistent
  3167. //
  3168. MprForgetRedirConnection(lpNameCopy);
  3169. status = WN_SUCCESS;
  3170. }
  3171. }
  3172. __except (EXCEPTION_EXECUTE_HANDLER)
  3173. {
  3174. status = WN_BAD_POINTER;
  3175. }
  3176. break;
  3177. }
  3178. default:
  3179. status = WN_BAD_VALUE;
  3180. }
  3181. LocalFree(lpNameCopy);
  3182. if (status != WN_SUCCESS)
  3183. {
  3184. SetLastError(status);
  3185. }
  3186. return status;
  3187. }
  3188. typedef LRESULT WINAPI FN_PostMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) ;
  3189. #ifdef UNICODE
  3190. #define USER32_DLL_NAME L"user32.dll"
  3191. #define POST_MSG_API_NAME "PostMessageW"
  3192. #else
  3193. #define USER32_DLL_NAME "user32.dll"
  3194. #define POST_MSG_API_NAME "PostMessageA"
  3195. #endif
  3196. static HMODULE _static_hUser32 = NULL;
  3197. static FN_PostMessage * _static_pfPostMsg = NULL;
  3198. VOID
  3199. DoRestoreConnection(
  3200. PARAMETERS *Params
  3201. )
  3202. /*++
  3203. Routine Description:
  3204. This function is run as a separate thread from the main thread (which
  3205. services a windows event loop). It attempts to restore all connections
  3206. that were saved in the registry for this user.
  3207. For each connection that we try to restore, a message is posted to
  3208. the main thread that causes it to put up a dialog box which describes
  3209. the connection and allows the user the option of cancelling.
  3210. If a longer timeout than the default is desired, this function will
  3211. look in the following location in the registry for a timeout value:
  3212. \HKEY_LOCAL_MACHINE\system\CurrentControlSet\Control\NetworkProvider
  3213. RestoreTimeout = REG_DWORD ??
  3214. --*/
  3215. {
  3216. DWORD status = WN_SUCCESS;
  3217. DWORD providerIndex=0;
  3218. DWORD Temp;
  3219. DWORD numSubKeys = Params->numSubKeys;
  3220. DWORD MaxWait = 0; // timeout interval
  3221. DWORD RegMaxWait = Params->RegMaxWait; // timeout interval stored in registry.
  3222. DWORD ElapsedTime; // interval between start and current time.
  3223. DWORD CurrentTime; // Current ClockTick time
  3224. DWORD StartTime;
  3225. DWORD i;
  3226. BOOL UserCancelled = FALSE;
  3227. BOOL ContinueFlag;
  3228. BOOL fDisconnect = FALSE;
  3229. LPCONNECTION_INFO ConnectArray = Params->ConnectArray;
  3230. HANDLE lpHandles[2];
  3231. DWORD dwSleepTime = RECONNECT_SLEEP_INCREMENT ;
  3232. DWORD j;
  3233. DWORD numStillMustContinue;
  3234. DWORD dwValue;
  3235. DWORD RestoredDrivesMask = 0;
  3236. DWORD CurrDriveMask;
  3237. LPWSTR lpLocalName;
  3238. WCHAR DriveLetterName[3]; // "<drive letter>:<NULL>"
  3239. HINSTANCE hDll = Params->hDll; // This needs to be checked
  3240. // Params is freed
  3241. //
  3242. // Don't check providers here as this isn't a top-level WNet API.
  3243. // No need to grab the shared lock here because the ConnectArray
  3244. // already contains all the entrypoints we need and the provider
  3245. // DLLs have all been refcounted.
  3246. //
  3247. lpHandles[0] = Params->hDlgCreated;
  3248. lpHandles[1] = Params->hDlgFailed;
  3249. StartTime = GetTickCount();
  3250. if (RegMaxWait != 0)
  3251. {
  3252. MaxWait = RegMaxWait;
  3253. }
  3254. if ( _static_pfPostMsg == NULL )
  3255. {
  3256. MprEnterLoadLibCritSect();
  3257. if (_static_hUser32 = LoadLibraryEx(USER32_DLL_NAME,
  3258. NULL,
  3259. LOAD_WITH_ALTERED_SEARCH_PATH))
  3260. {
  3261. _static_pfPostMsg = (FN_PostMessage *) GetProcAddress( _static_hUser32,
  3262. POST_MSG_API_NAME );
  3263. }
  3264. MprLeaveLoadLibCritSect();
  3265. if ( _static_pfPostMsg == NULL )
  3266. {
  3267. Params->status = GetLastError();
  3268. goto CleanExit;
  3269. }
  3270. }
  3271. if(Params->hDlg == INVALID_WINDOW_HANDLE)
  3272. {
  3273. dwValue = 0;
  3274. }
  3275. else
  3276. {
  3277. dwValue = WaitForMultipleObjects (2, lpHandles, FALSE, INFINITE);
  3278. }
  3279. switch(dwValue)
  3280. {
  3281. case 1: // hDlgFailed signaled. Info dialog failed to construct.
  3282. break;
  3283. case 0: // hDlgCreated signaled. Info dialog constructed successfully.
  3284. MPR_LOG0(RESTORE,"Enter Loop where we will attempt to restore connections\n");
  3285. do
  3286. {
  3287. //
  3288. // If HideErrors becomes TRUE, stop displaying error dialogs.
  3289. //
  3290. BOOL fHideErrors = (Params->dwRestoreFlags & WNRC_NOUI) ? TRUE : FALSE;
  3291. //
  3292. // Each time we go through all the connections, we need to
  3293. // reset the continue flag to FALSE.
  3294. //
  3295. ContinueFlag = FALSE;
  3296. //
  3297. // Go through each connection information in the array.
  3298. //
  3299. for (i = 0; i < numSubKeys; i++) {
  3300. // Let dialog thread print out the current connection.
  3301. if(Params->hDlg != INVALID_WINDOW_HANDLE)
  3302. {
  3303. (*_static_pfPostMsg) (Params->hDlg,
  3304. SHOW_CONNECTION,
  3305. (WPARAM) ConnectArray[i].NetResource.lpRemoteName,
  3306. 0);
  3307. }
  3308. //
  3309. // Setting status to SUCCESS forces us down the success path
  3310. // if we are not to continue adding this connection.
  3311. //
  3312. status = WN_SUCCESS;
  3313. if (ConnectArray[i].ContinueFlag)
  3314. {
  3315. status = MprRestoreThisConnection (NULL,
  3316. Params,
  3317. &(ConnectArray[i]),
  3318. Params->dwRestoreFlags);
  3319. }
  3320. switch (status)
  3321. {
  3322. case WN_SUCCESS:
  3323. ConnectArray[i].Status = WN_SUCCESS;
  3324. ConnectArray[i].ContinueFlag = FALSE;
  3325. if( g_LUIDDeviceMapsEnabled == TRUE )
  3326. {
  3327. lpLocalName = ConnectArray[i].NetResource.lpLocalName;
  3328. if( lpLocalName != NULL &&
  3329. wcslen(lpLocalName) == 2 &&
  3330. lpLocalName[1] == L':' &&
  3331. iswalpha(lpLocalName[0]) )
  3332. {
  3333. DriveLetterName[0] = RtlUpcaseUnicodeChar( lpLocalName[0] );
  3334. RestoredDrivesMask |= 1<<(DriveLetterName[0] - L'A');
  3335. }
  3336. }
  3337. break;
  3338. case WN_CONTINUE:
  3339. break;
  3340. case WN_NO_NETWORK:
  3341. case WN_FUNCTION_BUSY:
  3342. //
  3343. // If this is the first pass through, we don't have
  3344. // the wait times figured out for each provider. Do that
  3345. // now.
  3346. //
  3347. if (ConnectArray[i].ProviderWait == 0)
  3348. {
  3349. MPR_LOG0(RESTORE,"Ask provider how long it will take "
  3350. "for the net to come up\n");
  3351. Temp = ConnectArray[i].pfGetCaps(WNNC_START);
  3352. MPR_LOG2(RESTORE,"GetCaps(START) for Provider %ws yields %d\n",
  3353. ConnectArray[i].NetResource.lpProvider,
  3354. Temp)
  3355. switch (Temp)
  3356. {
  3357. case PROVIDER_WILL_NOT_START:
  3358. MPR_LOG0(RESTORE,"Provider will not start\n");
  3359. ConnectArray[i].ContinueFlag = FALSE;
  3360. ConnectArray[i].Status = status;
  3361. break;
  3362. case NO_TIME_ESTIMATE:
  3363. MPR_LOG0(RESTORE,"Provider doesn't have time estimate\n");
  3364. if (RegMaxWait != 0) {
  3365. ConnectArray[i].ProviderWait = RegMaxWait;
  3366. }
  3367. else {
  3368. ConnectArray[i].ProviderWait = DEFAULT_WAIT_TIME;
  3369. }
  3370. if (MaxWait < ConnectArray[i].ProviderWait) {
  3371. MaxWait = ConnectArray[i].ProviderWait;
  3372. }
  3373. break;
  3374. default:
  3375. MPR_LOG1(RESTORE,"Provider says it will take %d msec\n",
  3376. Temp);
  3377. if ((Temp <= MAX_ALLOWED_WAIT_TIME) && (Temp > MaxWait))
  3378. {
  3379. MaxWait = Temp;
  3380. }
  3381. ConnectArray[i].ProviderWait = MaxWait;
  3382. break;
  3383. }
  3384. }
  3385. //
  3386. // If the status for this provider has just changed to
  3387. // WN_FUNCTION_BUSY from some other status, then calculate
  3388. // a timeout time by getting the provider's new timeout
  3389. // and adding that to the elapsed time since start. This
  3390. // gives a total elapsed time until timeout - which can
  3391. // be compared with the current MaxWait.
  3392. //
  3393. if ((status == WN_FUNCTION_BUSY) &&
  3394. (ConnectArray[i].Status == WN_NO_NETWORK))
  3395. {
  3396. Temp = ConnectArray[i].pfGetCaps(WNNC_START);
  3397. MPR_LOG2(RESTORE,"Changed from NO_NET to BUSY\n"
  3398. "\tGetCaps(START) for Provider %ws yields %d\n",
  3399. ConnectArray[i].NetResource.lpProvider,
  3400. Temp);
  3401. switch (Temp)
  3402. {
  3403. case PROVIDER_WILL_NOT_START:
  3404. //
  3405. // This is bizzare to find the status = BUSY,
  3406. // but to have the Provider not starting.
  3407. //
  3408. ConnectArray[i].ContinueFlag = FALSE;
  3409. break;
  3410. case NO_TIME_ESTIMATE:
  3411. //
  3412. // No need to alter the timeout for this one.
  3413. //
  3414. break;
  3415. default:
  3416. //
  3417. // Make sure this new timeout information will take
  3418. // less than the maximum allowed time from providers.
  3419. //
  3420. if (Temp <= MAX_ALLOWED_WAIT_TIME)
  3421. {
  3422. CurrentTime = GetTickCount();
  3423. //
  3424. // Determine how much time has elapsed since
  3425. // we started.
  3426. //
  3427. ElapsedTime = CurrentTime - StartTime;
  3428. //
  3429. // Add the Elapsed time to the new timeout we
  3430. // just received from the provider to come up
  3431. // with a timeout value that can be compared
  3432. // with MaxWait.
  3433. //
  3434. Temp += ElapsedTime;
  3435. //
  3436. // If the new timeout is larger that MaxWait,
  3437. // then use the new timeout.
  3438. //
  3439. if (Temp > MaxWait)
  3440. {
  3441. MaxWait = Temp;
  3442. }
  3443. }
  3444. } // End Switch(Temp)
  3445. } // End If (change state from NO_NET to BUSY)
  3446. //
  3447. // Store the status (either NO_NET or BUSY) with the
  3448. // connect info.
  3449. //
  3450. if (ConnectArray[i].ContinueFlag)
  3451. {
  3452. ConnectArray[i].Status = status;
  3453. break;
  3454. }
  3455. case WN_CANCEL:
  3456. ConnectArray[i].Status = status;
  3457. default:
  3458. //
  3459. // For any other error, call the Error Dialog
  3460. //
  3461. Params->fReconnectFailed = TRUE;
  3462. if (fHideErrors) {
  3463. fDisconnect = FALSE;
  3464. } else {
  3465. //
  3466. // Count the number of connections which have not
  3467. // been resolved. If there is exactly one, do not
  3468. // give the user the option to cancel.
  3469. //
  3470. numStillMustContinue = 0;
  3471. for (j = 0; j < numSubKeys; j++) {
  3472. if (ConnectArray[j].ContinueFlag) {
  3473. numStillMustContinue++;
  3474. }
  3475. }
  3476. MPR_LOG1(RESTORE,"DoProfileErrorDialog with "
  3477. "%d connections remaining\n",
  3478. numStillMustContinue);
  3479. //
  3480. // We need to bump up the refcount for mprui.dll
  3481. // here since we're in a separate thread and
  3482. // WNetRestoreConnectionsW could return with this
  3483. // UI still up. If that happens and the process
  3484. // calls WNetClearConnections, we'll AV as soon as
  3485. // a message is sent to the UI window since
  3486. // WNetClearConnections unloads mprui.dll (this can
  3487. // happen in winlogon.exe). Note that this function
  3488. // will load mprui.dll globally if it hasn't already
  3489. // been done so after the first LoadLibrary call here,
  3490. // we're merely playing around with the DLL refcount
  3491. // rather than loading/unloading DLLs every time.
  3492. //
  3493. HINSTANCE hDll = LoadLibraryEx(L"mprui.dll",
  3494. NULL,
  3495. LOAD_WITH_ALTERED_SEARCH_PATH);
  3496. if (hDll == NULL)
  3497. {
  3498. MPR_LOG1(RESTORE,
  3499. "WNetRestoreConnectionW: loading mprui.dll failed %d\n",
  3500. GetLastError());
  3501. fDisconnect = FALSE;
  3502. }
  3503. else
  3504. {
  3505. DoProfileErrorDialog(
  3506. Params->hDlg == INVALID_WINDOW_HANDLE ?
  3507. NULL : Params->hDlg,
  3508. ConnectArray[i].NetResource.lpLocalName,
  3509. ConnectArray[i].NetResource.lpRemoteName,
  3510. ConnectArray[i].NetResource.lpProvider,
  3511. status,
  3512. (Params->hDlg != INVALID_WINDOW_HANDLE) &&
  3513. (numStillMustContinue > 1),
  3514. &UserCancelled,
  3515. &fDisconnect,
  3516. &fHideErrors);
  3517. FreeLibrary(hDll);
  3518. }
  3519. }
  3520. if (fDisconnect)
  3521. {
  3522. if (ConnectArray[i].NetResource.lpLocalName)
  3523. {
  3524. status = ConnectArray[i].pfCancelConnection(
  3525. ConnectArray[i].NetResource.lpLocalName,
  3526. TRUE);
  3527. }
  3528. else
  3529. {
  3530. status =
  3531. MprForgetPrintConnection(
  3532. ConnectArray[i].NetResource.lpRemoteName) ;
  3533. }
  3534. }
  3535. ConnectArray[i].ContinueFlag = FALSE;
  3536. break;
  3537. } // end switch(status)
  3538. ContinueFlag |= ConnectArray[i].ContinueFlag;
  3539. //
  3540. // If the User cancelled all further connection restoration
  3541. // work, then leave this loop.
  3542. //
  3543. if (UserCancelled)
  3544. {
  3545. status = WN_CANCEL;
  3546. ContinueFlag = FALSE;
  3547. break;
  3548. }
  3549. } // end For each connection.
  3550. if (ContinueFlag)
  3551. {
  3552. //
  3553. // Determine what the elapsed time from the start is.
  3554. //
  3555. CurrentTime = GetTickCount();
  3556. ElapsedTime = CurrentTime - StartTime;
  3557. //
  3558. // If a timeout occured, then don't continue. Otherwise, sleep for
  3559. // a bit and loop again through all connections.
  3560. //
  3561. if (ElapsedTime > MaxWait)
  3562. {
  3563. MPR_LOG0(RESTORE,"WNetRestoreConnectionW: Timed out while restoring\n");
  3564. ContinueFlag = FALSE;
  3565. status = WN_SUCCESS;
  3566. }
  3567. else
  3568. {
  3569. Sleep(dwSleepTime);
  3570. //
  3571. // increase sleeptime as we loop, but cap at 4 times
  3572. // the increment (currently that is 4 * 3 secs = 12 secs)
  3573. //
  3574. if (dwSleepTime < (RECONNECT_SLEEP_INCREMENT * 4))
  3575. {
  3576. dwSleepTime += RECONNECT_SLEEP_INCREMENT ;
  3577. }
  3578. }
  3579. }
  3580. } while (ContinueFlag);
  3581. break;
  3582. default:
  3583. status = GetLastError();
  3584. }
  3585. Params->status = status;
  3586. if(Params->hDlg != INVALID_WINDOW_HANDLE)
  3587. {
  3588. (*_static_pfPostMsg) (Params->hDlg, WM_QUIT, 0, 0);
  3589. }
  3590. CleanExit:
  3591. //
  3592. // Is this thread supposed to clean up?
  3593. //
  3594. if (InterlockedExchange(&Params->fDoCleanup, TRUE))
  3595. {
  3596. MPR_LOG0(RESTORE, "Worker thread will perform cleanup\n");
  3597. ASSERT(Params != NULL);
  3598. //
  3599. // Free up resources in preparation to return.
  3600. //
  3601. //
  3602. // If LUID device maps are enabled and we restored drive letter
  3603. // connections, then notify the shell about each restored drive
  3604. // letter connection.
  3605. //
  3606. if( (g_LUIDDeviceMapsEnabled == TRUE) &&
  3607. (RestoredDrivesMask != 0) )
  3608. {
  3609. CurrDriveMask = 1;
  3610. DriveLetterName[1] = L':';
  3611. DriveLetterName[2] = UNICODE_NULL;
  3612. for( DriveLetterName[0] = L'A';
  3613. DriveLetterName[0] <= L'Z';
  3614. DriveLetterName[0]++, CurrDriveMask <<= 1 )
  3615. {
  3616. if( CurrDriveMask & RestoredDrivesMask )
  3617. {
  3618. // Use the LUID broadcast mechanism
  3619. MprBroadcastDriveChange(
  3620. DriveLetterName,
  3621. FALSE ); // not a Delete Message
  3622. }
  3623. }
  3624. }
  3625. if (!CloseHandle(Params->hDlgCreated))
  3626. status = GetLastError();
  3627. if (!CloseHandle(Params->hDlgFailed))
  3628. status = GetLastError();
  3629. if (!CloseHandle(Params->hDonePassword))
  3630. status = GetLastError();
  3631. MprFreeConnectionArray(Params->ConnectArray, numSubKeys);
  3632. LocalFree(Params);
  3633. }
  3634. if (hDll != NULL)
  3635. {
  3636. //
  3637. // We have an HINSTANCE, so we're running in a
  3638. // separate thread
  3639. //
  3640. FreeLibraryAndExitThread(hDll, 0);
  3641. }
  3642. return;
  3643. }
  3644. BOOL
  3645. MprUserNameMatch (
  3646. IN PUNICODE_STRING DomainName,
  3647. IN PUNICODE_STRING UserName,
  3648. IN LPCWSTR RememberedName,
  3649. IN BOOL fMustMatchCompletely
  3650. )
  3651. /*++
  3652. Routine Description:
  3653. This function tests whether the user name for a remembered connection
  3654. matches the default user name.
  3655. Arguments:
  3656. DomainName - default logon domain
  3657. UserName - default logon user
  3658. RememberedName - user name remembered for the connection
  3659. Return Value:
  3660. TRUE if the name matches, FALSE otherwise.
  3661. --*/
  3662. {
  3663. if (IS_EMPTY_STRING(RememberedName))
  3664. {
  3665. return TRUE;
  3666. }
  3667. if (DomainName == NULL || UserName == NULL)
  3668. {
  3669. // This can happen if the LsaGetUserName call fails
  3670. return FALSE;
  3671. }
  3672. //
  3673. // If the remembered name is in the form "domain\user", we must compare
  3674. // against the full user name; otherwise, it's sufficient to compare
  3675. // against the unqualified user name
  3676. //
  3677. WCHAR * pSlash = wcschr(RememberedName, L'\\');
  3678. if (pSlash)
  3679. {
  3680. // Compare user name portion
  3681. UNICODE_STRING RememberedUserName;
  3682. RtlInitUnicodeString(&RememberedUserName, pSlash+1);
  3683. if (! RtlEqualUnicodeString(&RememberedUserName, UserName, TRUE))
  3684. {
  3685. return FALSE;
  3686. }
  3687. // Compare domain name portion
  3688. *pSlash = L'\0';
  3689. UNICODE_STRING RememberedDomainName;
  3690. RtlInitUnicodeString(&RememberedDomainName, RememberedName);
  3691. BOOL fMatch = RtlEqualUnicodeString(&RememberedDomainName, DomainName, TRUE);
  3692. *pSlash = L'\\';
  3693. return fMatch;
  3694. }
  3695. else if (fMustMatchCompletely)
  3696. {
  3697. //
  3698. // A complete match is required but there's no domain in RememberedName
  3699. //
  3700. return FALSE;
  3701. }
  3702. else
  3703. {
  3704. UNICODE_STRING RememberedUserName;
  3705. RtlInitUnicodeString(&RememberedUserName, RememberedName);
  3706. return (RtlEqualUnicodeString(&RememberedUserName, UserName, TRUE));
  3707. }
  3708. }
  3709. DWORD
  3710. MprRestoreThisConnection(
  3711. HWND hWnd,
  3712. PARAMETERS *Params,
  3713. LPCONNECTION_INFO ConnectInfo,
  3714. DWORD dwFlags
  3715. )
  3716. /*++
  3717. Routine Description:
  3718. This function attempts to add a single connection specified in the
  3719. ConnectInfo.
  3720. Arguments:
  3721. Params -
  3722. ConnectInfo - This is a pointer to a connection info structure which
  3723. contains all the information necessary to restore a network
  3724. connection.
  3725. Return Value:
  3726. returns whatever the providers AddConnection function returns.
  3727. --*/
  3728. {
  3729. DWORD status;
  3730. LPTSTR password=NULL;
  3731. HANDLE lpHandle;
  3732. BOOL fDidCancel;
  3733. TCHAR passwordBuffer[PWLEN+1] = {0};
  3734. LPWSTR UserNameForProvider;
  3735. BOOLEAN BufferAllocated = FALSE;
  3736. MPR_LOG3(RESTORE,
  3737. "Doing MprRestoreThisConnection for %ws, username = %ws, defer = %lu...\n",
  3738. ConnectInfo->NetResource.lpRemoteName,
  3739. ConnectInfo->UserName,
  3740. ConnectInfo->Defer);
  3741. //
  3742. // Pass the provider NULL as the user name on the first pass
  3743. // if the original connection used default creds.
  3744. //
  3745. if ( ConnectInfo->DeferFlags & DEFER_DEFAULT_CRED ) {
  3746. UserNameForProvider = NULL;
  3747. } else {
  3748. UserNameForProvider = ConnectInfo->UserName;
  3749. }
  3750. //
  3751. // Loop until we either have a successful connection, or
  3752. // until the user stops attempting to give a proper
  3753. // password.
  3754. //
  3755. do {
  3756. if (Params && Params->status == WN_CANCEL)
  3757. {
  3758. //
  3759. // User cancelled out on reconnections -- return
  3760. // WN_SUCCESS to let DoRestoreConnection finish
  3761. //
  3762. return WN_SUCCESS;
  3763. }
  3764. //
  3765. // Attempt to add the connection.
  3766. // NOTE: The initial password is NULL.
  3767. //
  3768. #if DBG == 1
  3769. DWORD ConnectTime = GetTickCount();
  3770. #endif
  3771. //**************************************
  3772. // Actual call to Provider
  3773. //**************************************
  3774. if (ConnectInfo->Defer)
  3775. {
  3776. ASSERT(ConnectInfo->pfAddConnection3 != NULL);
  3777. status = ConnectInfo->pfAddConnection3(
  3778. NULL, // hwndOwner
  3779. &(ConnectInfo->NetResource), // lpNetResource
  3780. password, // lpPassword
  3781. UserNameForProvider, // lpUserName
  3782. CONNECT_DEFERRED | (ConnectInfo->ProviderFlags << 24)
  3783. ); // dwFlags
  3784. }
  3785. else if (ConnectInfo->pfAddConnection != NULL)
  3786. {
  3787. status = ConnectInfo->pfAddConnection(
  3788. &(ConnectInfo->NetResource), // lpNetResource
  3789. password, // lpPassword
  3790. UserNameForProvider ); // lpUserName
  3791. }
  3792. else
  3793. {
  3794. status = WN_NOT_SUPPORTED;
  3795. }
  3796. #if DBG == 1
  3797. ConnectTime = GetTickCount() - ConnectTime;
  3798. MPR_LOG2(RESTORE, "...provider took %lu ms to return status %lu\n",
  3799. ConnectTime, status);
  3800. if (ConnectInfo->Defer && ConnectTime > 100)
  3801. {
  3802. DbgPrint("[MPR] ------ %ws took %lu ms to restore a DEFERRED\n"
  3803. " connection to %ws !\n",
  3804. ConnectInfo->NetResource.lpProvider, ConnectTime,
  3805. ConnectInfo->NetResource.lpRemoteName);
  3806. }
  3807. #endif
  3808. if (status == WN_CONNECTED_OTHER_PASSWORD_DEFAULT)
  3809. {
  3810. //
  3811. // Provider reconnected using default creds
  3812. //
  3813. status = WN_SUCCESS;
  3814. }
  3815. //
  3816. // If that fails due to a bad password, then
  3817. // loop until we either have a successful connection, or until
  3818. // the user stops attempting to give a proper password.
  3819. //
  3820. if (CREDUI_IS_AUTHENTICATION_ERROR(status))
  3821. {
  3822. //
  3823. // The password needs to be cleared each time around the loop,
  3824. // so that on subsequent add connections, we go back to the
  3825. // logon password.
  3826. //
  3827. password = NULL;
  3828. //
  3829. // If failure was due to bad password, then attempt
  3830. // to get a new password from the user.
  3831. //
  3832. // Changes made by congpay because of another thread.
  3833. if (Params == NULL) //lpDevice != NULL, restoring ONE
  3834. {
  3835. if (!(dwFlags & WNRC_NOUI))
  3836. {
  3837. //
  3838. // We need a username buffer to prompt for a username
  3839. //
  3840. if ( ConnectInfo->UserName == NULL ) {
  3841. ConnectInfo->UserName = (LPTSTR)LocalAlloc(LMEM_FIXED, CRED_MAX_USERNAME_LENGTH * sizeof(WCHAR));
  3842. if ( ConnectInfo->UserName == NULL ) {
  3843. status = ERROR_NOT_ENOUGH_MEMORY;
  3844. SetLastError (status);
  3845. memset(passwordBuffer, 0, sizeof(passwordBuffer)) ;
  3846. return status;
  3847. } else {
  3848. ConnectInfo->UserName[0] = L'\0';
  3849. BufferAllocated = TRUE;
  3850. }
  3851. }
  3852. //
  3853. // Prompt for a username
  3854. //
  3855. status = DoPasswordDialog(hWnd,
  3856. ConnectInfo->NetResource.lpRemoteName,
  3857. ConnectInfo->UserName,
  3858. passwordBuffer,
  3859. sizeof (passwordBuffer),
  3860. &fDidCancel,
  3861. status);
  3862. //
  3863. // Convert zero length username to NULL buffer
  3864. //
  3865. if ( BufferAllocated && status == NO_ERROR ) {
  3866. //
  3867. // If there is no user name (the length is 0), then set the
  3868. // return pointer to NULL.
  3869. //
  3870. if (STRLEN(ConnectInfo->UserName) == 0) {
  3871. LocalFree(ConnectInfo->UserName);
  3872. ConnectInfo->UserName = NULL;
  3873. BufferAllocated = FALSE;
  3874. }
  3875. }
  3876. }
  3877. if (status != WN_SUCCESS)
  3878. {
  3879. SetLastError (status);
  3880. memset(passwordBuffer, 0, sizeof(passwordBuffer)) ;
  3881. return status;
  3882. }
  3883. else
  3884. {
  3885. if (fDidCancel)
  3886. {
  3887. status = WN_CANCEL;
  3888. }
  3889. else
  3890. {
  3891. password = passwordBuffer;
  3892. status = WN_ACCESS_DENIED;
  3893. }
  3894. }
  3895. }
  3896. else // restoring all
  3897. {
  3898. if (!(Params->dwRestoreFlags & WNRC_NOUI))
  3899. {
  3900. if (Params->status == WN_CANCEL)
  3901. {
  3902. //
  3903. // User cancelled out of restoring connections. Return
  3904. // WN_SUCCESS to let DoRestoreConnection finish looping
  3905. //
  3906. continue;
  3907. }
  3908. if ( _static_pfPostMsg == NULL ) {
  3909. MprEnterLoadLibCritSect();
  3910. if ( _static_hUser32 = LoadLibraryEx(
  3911. USER32_DLL_NAME,
  3912. NULL,
  3913. LOAD_WITH_ALTERED_SEARCH_PATH ) ) {
  3914. _static_pfPostMsg = (FN_PostMessage *) GetProcAddress( _static_hUser32,
  3915. POST_MSG_API_NAME );
  3916. }
  3917. MprLeaveLoadLibCritSect();
  3918. if ( _static_pfPostMsg == NULL ) {
  3919. memset(passwordBuffer, 0, sizeof(passwordBuffer)) ;
  3920. return GetLastError();
  3921. }
  3922. }
  3923. lpHandle = Params->hDonePassword;
  3924. Params->pchResource = ConnectInfo->NetResource.lpRemoteName;
  3925. Params->pchUserName = ConnectInfo->UserName;
  3926. Params->dwError = status;
  3927. if ((Params->hDlg == INVALID_WINDOW_HANDLE)
  3928. ||
  3929. ((*_static_pfPostMsg)
  3930. (Params->hDlg,
  3931. DO_PASSWORD_DIALOG,
  3932. (WPARAM) Params,
  3933. 0) == 0))
  3934. {
  3935. //
  3936. // Either we're not using a credential dialog or the
  3937. // PostMessage call failed -- bail.
  3938. //
  3939. MPR_LOG3(ERROR,
  3940. "%ws returned %d for connection to %ws -- bailing\n",
  3941. ConnectInfo->NetResource.lpProvider,
  3942. status,
  3943. ConnectInfo->NetResource.lpRemoteName);
  3944. goto CredentialError;
  3945. }
  3946. WaitForSingleObject ( lpHandle, INFINITE );
  3947. if (Params->status == WN_SUCCESS)
  3948. {
  3949. if (Params->fDidCancel)
  3950. {
  3951. status = WN_CANCEL;
  3952. }
  3953. else
  3954. {
  3955. password = Params->passwordBuffer;
  3956. }
  3957. }
  3958. else
  3959. {
  3960. status = Params->status;
  3961. }
  3962. }
  3963. else
  3964. {
  3965. // Caller wants no UI - don't show any
  3966. status = WN_CANCEL;
  3967. // Remember that we had a failure to reconnect
  3968. Params->fReconnectFailed = TRUE;
  3969. }
  3970. }
  3971. }
  3972. else if (status == WN_SUCCESS)
  3973. {
  3974. DWORD dwIgnoredStatus;
  3975. MPR_LOG1(RESTORE,"MprRestoreThisConnection: Successful "
  3976. "restore of connection for %ws\n",
  3977. ConnectInfo->NetResource.lpRemoteName);
  3978. //
  3979. // Username for the connection might have changed via the
  3980. // password dialog -- update it.
  3981. //
  3982. dwIgnoredStatus = MprSetRegValue(ConnectInfo->RegKey,
  3983. USER_NAME,
  3984. ConnectInfo->UserName,
  3985. 0);
  3986. if (dwIgnoredStatus != ERROR_SUCCESS)
  3987. {
  3988. MPR_LOG(ERROR,
  3989. "MprSetRegValue for user name failed %lu\n",
  3990. dwIgnoredStatus);
  3991. }
  3992. //
  3993. // If the DEFER_UNKNOWN flag was set, we can clear it now.
  3994. // If the default password worked, we know that we can defer the
  3995. // connection in future. If we had to prompt for a password, we
  3996. // can't defer the connection in future.
  3997. // Note: This may not do the right thing for share-level
  3998. // connections where the level of access depends on the password.
  3999. // But we'll let users fix that by manually recreating the
  4000. // connection.
  4001. //
  4002. if (ConnectInfo->DeferFlags & DEFER_UNKNOWN)
  4003. {
  4004. if (password == NULL)
  4005. {
  4006. ConnectInfo->DeferFlags &=
  4007. ~(DEFER_UNKNOWN | DEFER_EXPLICIT_PASSWORD);
  4008. MPR_LOG1(RESTORE,"MprRestoreThisConnection: WILL defer "
  4009. "connection for %ws in future\n",
  4010. ConnectInfo->NetResource.lpRemoteName);
  4011. }
  4012. else
  4013. {
  4014. ConnectInfo->DeferFlags &= ~DEFER_UNKNOWN;
  4015. ConnectInfo->DeferFlags |= DEFER_EXPLICIT_PASSWORD;
  4016. MPR_LOG1(RESTORE,"MprRestoreThisConnection: will NOT defer "
  4017. "connection for %ws in future\n",
  4018. ConnectInfo->NetResource.lpRemoteName);
  4019. }
  4020. dwIgnoredStatus = MprSaveDeferFlags(ConnectInfo->RegKey,
  4021. ConnectInfo->DeferFlags);
  4022. if (dwIgnoredStatus != ERROR_SUCCESS)
  4023. {
  4024. MPR_LOG(ERROR, "MprSaveDeferFlags failed %lu\n", dwIgnoredStatus);
  4025. }
  4026. }
  4027. }
  4028. else
  4029. {
  4030. //
  4031. // An unexpected error occured. In this case,
  4032. // we want to leave the loop.
  4033. //
  4034. MPR_LOG2(ERROR,
  4035. "MprRestoreThisConnection: AddConnection for (%ws) Error %d \n",
  4036. ConnectInfo->NetResource.lpProvider,
  4037. status);
  4038. break;
  4039. }
  4040. //
  4041. // On subsequent iterations,
  4042. // pass the provider the user name typed by the caller.
  4043. //
  4044. UserNameForProvider = ConnectInfo->UserName;
  4045. }
  4046. while (CREDUI_IS_AUTHENTICATION_ERROR(status));
  4047. CredentialError:
  4048. memset(passwordBuffer, 0, sizeof(passwordBuffer)) ;
  4049. return status;
  4050. }
  4051. DWORD
  4052. MprCreateConnectionArray(
  4053. LPDWORD lpNumConnections,
  4054. LPCTSTR lpDevice,
  4055. LPDWORD lpRegMaxWait,
  4056. LPCONNECTION_INFO *ConnectArray
  4057. )
  4058. /*++
  4059. Routine Description:
  4060. This function creates an array of CONNECTION_INFO structures and fills
  4061. each element in the array with the info that is stored in the registry
  4062. for that connection.
  4063. NOTE: This function allocates memory for the array.
  4064. Arguments:
  4065. NumConnections - This is a pointer to the place where the number of
  4066. connections is to be placed. This indicates how many elements
  4067. are stored in the array.
  4068. lpDevice - If this is NULL, information on all remembered connections
  4069. is required. Otherwise, information for only the lpDevice connection
  4070. is required.
  4071. lpRegMaxWait - This is a pointer to the location where the wait time
  4072. read from the registry is to be placed. If this value does not
  4073. exist in the registry, then the returned value is 0.
  4074. ConnectArray - This is a pointer to the location where the pointer to
  4075. the array is to be placed.
  4076. Return Value:
  4077. An error status code is returned only if something happens that will not
  4078. allow us to restore even one connection.
  4079. --*/
  4080. {
  4081. DWORD status = WN_SUCCESS;
  4082. HKEY connectHandle;
  4083. HKEY providerKeyHandle;
  4084. DWORD maxSubKeyLen;
  4085. DWORD maxValueLen;
  4086. DWORD ValueType;
  4087. DWORD Temp;
  4088. DWORD i;
  4089. BOOL AtLeastOneSuccess = FALSE;
  4090. //
  4091. // init return data
  4092. //
  4093. *lpNumConnections = 0 ;
  4094. *ConnectArray = NULL ;
  4095. //
  4096. // Get a handle for the connection section of the user's registry
  4097. // space.
  4098. //
  4099. if (!MprOpenKey(
  4100. HKEY_CURRENT_USER,
  4101. CONNECTION_KEY_NAME,
  4102. &connectHandle,
  4103. DA_READ)) {
  4104. MPR_LOG(ERROR,"WNetRestoreConnection: MprOpenKey Failed\n",0);
  4105. return(WN_CANNOT_OPEN_PROFILE);
  4106. }
  4107. //
  4108. // Find out the number of connections to restore (numSubKeys) and
  4109. // the max lengths of subkeys and values.
  4110. //
  4111. if(!MprGetKeyInfo(
  4112. connectHandle,
  4113. NULL,
  4114. lpNumConnections,
  4115. &maxSubKeyLen,
  4116. NULL,
  4117. &maxValueLen))
  4118. {
  4119. MPR_LOG(ERROR,"WNetRestoreConnection: MprGetKeyInfo Failed\n",0);
  4120. *lpNumConnections = 0 ;
  4121. RegCloseKey(connectHandle);
  4122. return(WN_CANNOT_OPEN_PROFILE);
  4123. }
  4124. if (*lpNumConnections == 0) {
  4125. RegCloseKey(connectHandle);
  4126. return(WN_SUCCESS);
  4127. }
  4128. if (lpDevice != NULL) {
  4129. *lpNumConnections = 1;
  4130. }
  4131. //
  4132. // Allocate the array.
  4133. //
  4134. *ConnectArray = (LPCONNECTION_INFO)LocalAlloc(
  4135. LPTR,
  4136. *lpNumConnections * sizeof(CONNECTION_INFO));
  4137. if (*ConnectArray == NULL) {
  4138. *lpNumConnections = 0 ;
  4139. RegCloseKey(connectHandle);
  4140. return(GetLastError());
  4141. }
  4142. //
  4143. // Level 1 initialization for the call to MprGetProviderIndex in the loop
  4144. //
  4145. if (!(GlobalInitLevel & FIRST_LEVEL)) {
  4146. status = MprLevel1Init();
  4147. if (status != WN_SUCCESS) {
  4148. RegCloseKey(connectHandle);
  4149. return status;
  4150. }
  4151. }
  4152. for (i=0; i < *lpNumConnections; i++) {
  4153. //
  4154. // Read a Connection Key and accompanying information from the
  4155. // registry.
  4156. //
  4157. // NOTE: If successful, this function will allocate memory for
  4158. // netResource.lpRemoteName,
  4159. // netResource.lpProvider,
  4160. // netResource.lpLocalName, and optionally....
  4161. // userName
  4162. //
  4163. if (!MprReadConnectionInfo(
  4164. connectHandle,
  4165. lpDevice,
  4166. i,
  4167. &((*ConnectArray)[i].ProviderFlags),
  4168. &((*ConnectArray)[i].DeferFlags),
  4169. &((*ConnectArray)[i].UserName),
  4170. &((*ConnectArray)[i].NetResource),
  4171. &((*ConnectArray)[i].RegKey),
  4172. maxSubKeyLen)) {
  4173. //
  4174. // The read failed even though this should be a valid index.
  4175. //
  4176. MPR_LOG0(ERROR,
  4177. "MprCreateConnectionArray: ReadConnectionInfo Failed\n");
  4178. status = WN_CANNOT_OPEN_PROFILE;
  4179. }
  4180. else {
  4181. //
  4182. // Get the Provider Index
  4183. //
  4184. if (MprGetProviderIndex(
  4185. (*ConnectArray)[i].NetResource.lpProvider,
  4186. &((*ConnectArray)[i].ProviderIndex))) {
  4187. AtLeastOneSuccess = TRUE;
  4188. (*ConnectArray)[i].ContinueFlag = TRUE;
  4189. }
  4190. else {
  4191. //
  4192. // The provider index could not be found. This may mean
  4193. // that the provider information stored in the registry
  4194. // is for a provider that is no longer in the ProviderOrder
  4195. // list. (The provider has been removed). In that case,
  4196. // we will just skip this provider. We will leave the
  4197. // ContinueFlag set to 0 (FALSE).
  4198. //
  4199. MPR_LOG0(ERROR,
  4200. "MprCreateConnectionArray:MprGetProviderIndex Failed\n");
  4201. status = WN_BAD_PROVIDER;
  4202. (*ConnectArray)[i].Status = status;
  4203. }
  4204. } // endif (MprReadConnectionInfo)
  4205. } // endfor (i=0; i<numSubKeys)
  4206. if (!AtLeastOneSuccess) {
  4207. //
  4208. // If we gather any connection information, return the last error
  4209. // that occured.
  4210. //
  4211. MprFreeConnectionArray(*ConnectArray,*lpNumConnections);
  4212. *ConnectArray = NULL ;
  4213. *lpNumConnections = 0 ;
  4214. RegCloseKey(connectHandle);
  4215. goto CleanExit;
  4216. }
  4217. RegCloseKey(connectHandle);
  4218. //
  4219. // Read the MaxWait value that is stored in the registry.
  4220. // If it is not there or if the value is less than our default
  4221. // maximum value, then use the default instead.
  4222. //
  4223. if(!MprOpenKey(
  4224. HKEY_LOCAL_MACHINE, // hKey
  4225. NET_PROVIDER_KEY, // lpSubKey
  4226. &providerKeyHandle, // Newly Opened Key Handle
  4227. DA_READ)) { // Desired Access
  4228. MPR_LOG(ERROR,"MprCreateConnectionArray: MprOpenKey (%ws) Error\n",
  4229. NET_PROVIDER_KEY);
  4230. *lpRegMaxWait = 0;
  4231. status = WN_SUCCESS;
  4232. goto CleanExit;
  4233. }
  4234. MPR_LOG(TRACE,"OpenKey %ws\n, ",NET_PROVIDER_KEY);
  4235. Temp = sizeof(*lpRegMaxWait);
  4236. status = RegQueryValueEx(
  4237. providerKeyHandle,
  4238. RESTORE_WAIT_VALUE,
  4239. NULL,
  4240. &ValueType,
  4241. (LPBYTE)lpRegMaxWait,
  4242. &Temp);
  4243. RegCloseKey(providerKeyHandle);
  4244. if (status != NO_ERROR) {
  4245. *lpRegMaxWait = 0;
  4246. }
  4247. status = WN_SUCCESS;
  4248. CleanExit:
  4249. return(WN_SUCCESS);
  4250. }
  4251. VOID
  4252. MprFreeConnectionArray(
  4253. LPCONNECTION_INFO ConnectArray,
  4254. DWORD NumConnections
  4255. )
  4256. /*++
  4257. Routine Description:
  4258. This function frees up all the elements in the connection array, and
  4259. finally frees the array itself.
  4260. Arguments:
  4261. Return Value:
  4262. none
  4263. --*/
  4264. {
  4265. DWORD status = WN_SUCCESS;
  4266. LPNETRESOURCEW netResource;
  4267. DWORD i;
  4268. for (i=0; i<NumConnections; i++)
  4269. {
  4270. netResource = &(ConnectArray[i].NetResource);
  4271. //
  4272. // Free the allocated memory resources.
  4273. //
  4274. LocalFree(netResource->lpLocalName);
  4275. LocalFree(netResource->lpRemoteName);
  4276. LocalFree(netResource->lpProvider);
  4277. LocalFree(ConnectArray[i].UserName);
  4278. if (ConnectArray[i].RegKey != NULL)
  4279. {
  4280. RegCloseKey(ConnectArray[i].RegKey);
  4281. }
  4282. if (ConnectArray[i].hProviderDll != NULL)
  4283. {
  4284. FreeLibrary(ConnectArray[i].hProviderDll);
  4285. }
  4286. }
  4287. LocalFree(ConnectArray);
  4288. return;
  4289. }
  4290. DWORD
  4291. MprNotifyErrors(
  4292. HWND hWnd,
  4293. LPCONNECTION_INFO ConnectArray,
  4294. DWORD NumConnections,
  4295. DWORD dwFlags
  4296. )
  4297. /*++
  4298. Routine Description:
  4299. This function calls the error dialog for each connection that still
  4300. has the continue flag set, and does not have a SUCCESS status.
  4301. Arguments:
  4302. hWnd - This is a window handle that will be used as owner of the
  4303. error dialog.
  4304. ConnectArray - This is the array of connection information.
  4305. At the point when this function is called, the following fields
  4306. are meaningful:
  4307. ContinueFlag - If set, it means that this connection has not yet
  4308. been established.
  4309. StatusFlag - If this is not SUCCESS, then it contains the error
  4310. status from the last call to the provider.
  4311. ContinueFlag Status
  4312. ---------------|---------------
  4313. | FALSE | NotSuccess | Provider will not start
  4314. | FALSE | Success | Connection was successfully established
  4315. | TRUE | NotSuccess | Time-out occured
  4316. | TRUE | Success | This can never occur.
  4317. -------------------------------
  4318. NumConnections - This is the number of entries in the array of
  4319. connection information.
  4320. Return Value:
  4321. --*/
  4322. {
  4323. DWORD i;
  4324. BOOL fDisconnect = FALSE;
  4325. DWORD status = WN_SUCCESS;
  4326. //
  4327. // If HideErrors becomes TRUE, stop displaying error dialogs
  4328. //
  4329. BOOL fHideErrors = (dwFlags & WNRC_NOUI) ? TRUE : FALSE;
  4330. for (i=0; (i<NumConnections) && (!fHideErrors); i++ )
  4331. {
  4332. if ((ConnectArray[i].ContinueFlag) &&
  4333. (ConnectArray[i].Status != WN_SUCCESS) &&
  4334. (ConnectArray[i].Status != WN_CANCEL) &&
  4335. (ConnectArray[i].Status != WN_CONTINUE))
  4336. {
  4337. //
  4338. // For any other error, call the Error Dialog
  4339. //
  4340. DoProfileErrorDialog (
  4341. hWnd,
  4342. ConnectArray[i].NetResource.lpLocalName,
  4343. ConnectArray[i].NetResource.lpRemoteName,
  4344. ConnectArray[i].NetResource.lpProvider,
  4345. ConnectArray[i].Status,
  4346. FALSE, //No cancel button.
  4347. NULL,
  4348. &fDisconnect,
  4349. &fHideErrors);
  4350. if (fDisconnect)
  4351. {
  4352. status = ConnectArray[i].pfCancelConnection(
  4353. ConnectArray[i].NetResource.lpLocalName,
  4354. TRUE);
  4355. }
  4356. }
  4357. }
  4358. return status;
  4359. }
  4360. DWORD
  4361. MprAddPrintersToConnArray(
  4362. LPDWORD lpNumConnections,
  4363. LPCONNECTION_INFO *ConnectArray
  4364. )
  4365. /*++
  4366. Routine Description:
  4367. This function augments the array of CONNECTION_INFO with print connections.
  4368. NOTE: This function allocates memory for the array if need.
  4369. Arguments:
  4370. NumConnections - This is a pointer to the place where the number of
  4371. connections is to be placed. This indicates how many elements
  4372. are stored in the array.
  4373. ConnectArray - This is a pointer to the location where the pointer to
  4374. the array is to be placed.
  4375. Return Value:
  4376. An error status code is returned only if something happens that will not
  4377. allow us to restore even one connection.
  4378. --*/
  4379. {
  4380. DWORD status = WN_SUCCESS;
  4381. HKEY connectHandle;
  4382. DWORD i,j;
  4383. DWORD NumValueNames ;
  4384. DWORD MaxValueNameLength;
  4385. DWORD MaxValueLen ;
  4386. LPNETRESOURCE lpNetResource ;
  4387. LPWSTR lpUserName = NULL ;
  4388. LPWSTR lpProviderName = NULL ;
  4389. LPWSTR lpRemoteName = NULL ;
  4390. LPBYTE lpBuffer = NULL ;
  4391. //
  4392. // Get a handle for the connection section of the user's registry
  4393. // space.
  4394. //
  4395. if (!MprOpenKey(
  4396. HKEY_CURRENT_USER,
  4397. PRINT_CONNECTION_KEY_NAME,
  4398. &connectHandle,
  4399. DA_READ))
  4400. {
  4401. return(WN_SUCCESS); // ignore the restored connections.
  4402. }
  4403. //
  4404. // Find out the number of connections to restore and
  4405. // the max lengths of names and values.
  4406. //
  4407. status = MprGetPrintKeyInfo(connectHandle,
  4408. &NumValueNames,
  4409. &MaxValueNameLength,
  4410. &MaxValueLen) ;
  4411. if (status != WN_SUCCESS || NumValueNames == 0)
  4412. {
  4413. //
  4414. // ignore the restored connections, or nothing to add
  4415. //
  4416. RegCloseKey(connectHandle);
  4417. return(WN_SUCCESS);
  4418. }
  4419. //
  4420. // Allocate the array and copy over the info if previous pointer not null.
  4421. //
  4422. lpBuffer = (LPBYTE) LocalAlloc(LPTR,
  4423. (*lpNumConnections + NumValueNames) *
  4424. sizeof(CONNECTION_INFO)) ;
  4425. if (lpBuffer == NULL)
  4426. {
  4427. RegCloseKey(connectHandle);
  4428. return(GetLastError());
  4429. }
  4430. if (*ConnectArray)
  4431. {
  4432. memcpy(lpBuffer,
  4433. *ConnectArray,
  4434. (*lpNumConnections * sizeof(CONNECTION_INFO))) ;
  4435. LocalFree (*ConnectArray) ;
  4436. }
  4437. //
  4438. // set j to index from previous location, update the count and pointer.
  4439. // then loop thru all new entries, adding to the connect array.
  4440. //
  4441. j = *lpNumConnections ;
  4442. *lpNumConnections += NumValueNames ;
  4443. *ConnectArray = (CONNECTION_INFO *) lpBuffer ;
  4444. //
  4445. // Level 1 initialization for the call to MprGetProviderIndex in the loop
  4446. //
  4447. if (!(GlobalInitLevel & FIRST_LEVEL)) {
  4448. status = MprLevel1Init();
  4449. if (status != WN_SUCCESS) {
  4450. RegCloseKey(connectHandle);
  4451. return status;
  4452. }
  4453. }
  4454. for (i=0; i < NumValueNames; i++, j++)
  4455. {
  4456. DWORD TypeCode ;
  4457. DWORD cbRemoteName = (MaxValueNameLength + 1) * sizeof (WCHAR) ;
  4458. DWORD cbProviderName = MaxValueLen ;
  4459. //
  4460. // allocate the strings for the providername, remotename
  4461. //
  4462. if (!(lpProviderName = (LPWSTR) LocalAlloc(0, cbProviderName )))
  4463. {
  4464. status = GetLastError() ;
  4465. goto ErrorExit ;
  4466. }
  4467. if (!(lpRemoteName = (LPWSTR) LocalAlloc(0, cbRemoteName )))
  4468. {
  4469. status = GetLastError() ;
  4470. goto ErrorExit ;
  4471. }
  4472. //
  4473. // Init the rest. Username currently not set by system, so always NULL
  4474. //
  4475. lpUserName = NULL ;
  4476. lpNetResource = &(*ConnectArray)[j].NetResource ;
  4477. lpNetResource->lpLocalName = NULL ;
  4478. lpNetResource->lpRemoteName = lpRemoteName ;
  4479. lpNetResource->lpProvider = lpProviderName ;
  4480. lpNetResource->dwType = 0 ;
  4481. //
  4482. // null these so we dont free twice if error exit later
  4483. //
  4484. lpRemoteName = NULL ;
  4485. lpProviderName = NULL ;
  4486. status = RegEnumValue(connectHandle,
  4487. i,
  4488. lpNetResource->lpRemoteName,
  4489. &cbRemoteName,
  4490. 0,
  4491. &TypeCode,
  4492. (LPBYTE) lpNetResource->lpProvider,
  4493. &cbProviderName) ;
  4494. if (status == NO_ERROR)
  4495. {
  4496. (*ConnectArray)[j].UserName = lpUserName ;
  4497. //
  4498. // Get the Provider Index
  4499. //
  4500. if (MprGetProviderIndex(
  4501. (*ConnectArray)[j].NetResource.lpProvider,
  4502. &((*ConnectArray)[j].ProviderIndex)))
  4503. {
  4504. (*ConnectArray)[j].ContinueFlag = TRUE;
  4505. }
  4506. else
  4507. {
  4508. //
  4509. // The provider index could not be found. This may mean
  4510. // that the provider information stored in the registry
  4511. // is for a provider that is no longer in the ProviderOrder
  4512. // list. (The provider has been removed). In that case,
  4513. // we will just skip this provider. We will leave the
  4514. // ContinueFlag set to 0 (FALSE).
  4515. //
  4516. status = WN_BAD_PROVIDER;
  4517. (*ConnectArray)[j].Status = status;
  4518. }
  4519. }
  4520. else
  4521. {
  4522. //
  4523. // should not happen, but if it does the array is half built,
  4524. // and cannot be used, so ErrorExit (this will clean it up).
  4525. //
  4526. goto ErrorExit ;
  4527. }
  4528. }
  4529. RegCloseKey(connectHandle);
  4530. return(WN_SUCCESS);
  4531. ErrorExit:
  4532. RegCloseKey(connectHandle);
  4533. LocalFree(lpProviderName) ;
  4534. LocalFree(lpRemoteName) ;
  4535. MprFreeConnectionArray(*ConnectArray,*lpNumConnections);
  4536. *ConnectArray = NULL ;
  4537. *lpNumConnections = 0 ;
  4538. return status;
  4539. }
  4540. VOID
  4541. MprNotifyShell(
  4542. IN LPCWSTR pwszDevice
  4543. )
  4544. /*++
  4545. Routine Description:
  4546. This function sets an event that asks a trusted system component
  4547. (the service controller) to discover the changed network drives and
  4548. asynchronously broadcast a device change message on our behalf.
  4549. CODEWORK: Replace this entire mechanism with real plug-n-play.
  4550. Arguments:
  4551. pwszDevice - Name of the local device (NULL for UNC connections)
  4552. Return Value:
  4553. None
  4554. History:
  4555. BruceFo 19-May-1995 Created, calls BSM directly
  4556. AnirudhS 06-Jun-1996 Set event to have another component do
  4557. the BSM on our behalf
  4558. --*/
  4559. {
  4560. // The shell is only interested in drive redirections
  4561. if (pwszDevice == NULL || wcslen(pwszDevice) != 2 || pwszDevice[1] != L':')
  4562. {
  4563. return;
  4564. }
  4565. // Ask for a device change message to be broadcast
  4566. HANDLE hBSMEvent = OpenEvent(
  4567. EVENT_MODIFY_STATE, // desired access
  4568. FALSE, // don't inherit
  4569. SC_BSM_EVENT_NAME // name
  4570. );
  4571. if (hBSMEvent == NULL)
  4572. {
  4573. MPR_LOG(ERROR, "Couldn't open event for BSM request, %lu\n",
  4574. GetLastError());
  4575. }
  4576. else
  4577. {
  4578. if (! SetEvent(hBSMEvent))
  4579. {
  4580. MPR_LOG(ERROR, "Couldn't set event for BSM request, %lu\n",
  4581. GetLastError());
  4582. }
  4583. CloseHandle(hBSMEvent);
  4584. }
  4585. }
  4586. BOOL
  4587. MprBroadcastDriveChange(
  4588. IN LPCWSTR pwszDevice,
  4589. IN BOOL DeleteMessage
  4590. )
  4591. /*++
  4592. Routine Description:
  4593. This function asynchronously broadcasts a device change message on
  4594. our behalf.
  4595. Arguments:
  4596. pwszDevice - Name of the local device (NULL for UNC connections)
  4597. DeleteMessage - denotes where a delete/add message is needed
  4598. TRUE - send a device deleted message
  4599. FALSE - send a device added message
  4600. Return Value:
  4601. TRUE - Operations completed
  4602. FALSE - Error encountered
  4603. --*/
  4604. {
  4605. BOOL Result;
  4606. DWORD dwFlags = DDD_LUID_BROADCAST_DRIVE;
  4607. // The shell is only interested in drive redirections
  4608. if (pwszDevice == NULL || wcslen(pwszDevice) != 2 || pwszDevice[1] != L':')
  4609. {
  4610. return( FALSE );
  4611. }
  4612. if( DeleteMessage == TRUE )
  4613. {
  4614. dwFlags |= DDD_REMOVE_DEFINITION;
  4615. }
  4616. Result = DefineDosDeviceW( dwFlags, pwszDevice, NULL );
  4617. return( Result );
  4618. }