Leaked source code of windows server 2003
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.

5610 lines
177 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(MPR_EXCEPTION_FILTER)
  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 (MPR_EXCEPTION_FILTER)
  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(MPR_EXCEPTION_FILTER)
  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(MPR_EXCEPTION_FILTER) {
  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. {
  2540. MprFreeConnectionArray(ConnectArray, numSubKeys);
  2541. SetLastError(WN_NO_NETWORK);
  2542. return(WN_NO_NETWORK);
  2543. }
  2544. //
  2545. // Refcount all the provider DLLs we may use since we may
  2546. // have to call DoProfileErrorDialog, which calls outside
  2547. // mpr.dll and can potentially loop back, causing deadlock.
  2548. // By refcounting here, we don't have to worry about
  2549. // releasing/reacquiring the lock or over-refcounting the
  2550. // provider DLLs later on.
  2551. //
  2552. MprRefcountConnectionArray(ConnectArray, numSubKeys);
  2553. }
  2554. // If lpDevice is not NULL, call MprRestoreThisConnection directly.
  2555. if (lpDevice)
  2556. {
  2557. status = MprRestoreThisConnection (hWnd, NULL, &ConnectArray[0], dwFlags);
  2558. ConnectArray[0].Status = status;
  2559. if ((status != WN_SUCCESS) &&
  2560. (status != WN_CANCEL) &&
  2561. (status != WN_CONTINUE))
  2562. {
  2563. if (!(dwFlags & WNRC_NOUI))
  2564. {
  2565. DoProfileErrorDialog (hWnd,
  2566. ConnectArray[0].NetResource.lpLocalName,
  2567. ConnectArray[0].NetResource.lpRemoteName,
  2568. ConnectArray[0].NetResource.lpProvider,
  2569. ConnectArray[0].Status,
  2570. FALSE, //No cancel button.
  2571. NULL,
  2572. NULL,
  2573. NULL); // no skip future errors checkbox
  2574. }
  2575. if (pfReconnectFailed)
  2576. {
  2577. *pfReconnectFailed = TRUE;
  2578. }
  2579. }
  2580. //
  2581. // ConnectArray isn't used any more for the single-drive case
  2582. //
  2583. MprFreeConnectionArray(ConnectArray, numSubKeys);
  2584. }
  2585. else do // not a loop. error break out.
  2586. {
  2587. //
  2588. // Initialize lpParams.
  2589. //
  2590. lpParams = (PARAMETERS *) LocalAlloc (LPTR,
  2591. sizeof (PARAMETERS));
  2592. if ((lpParams == NULL) ||
  2593. (lpParams->hDlgCreated
  2594. = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL ||
  2595. (lpParams->hDlgFailed
  2596. = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL ||
  2597. (lpParams->hDonePassword
  2598. = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL)
  2599. {
  2600. status = GetLastError();
  2601. if (lpParams != NULL)
  2602. {
  2603. if (lpParams->hDlgCreated != NULL)
  2604. CloseHandle(lpParams->hDlgCreated);
  2605. if (lpParams->hDlgFailed != NULL)
  2606. CloseHandle(lpParams->hDlgFailed);
  2607. if (lpParams->hDonePassword != NULL)
  2608. CloseHandle(lpParams->hDonePassword);
  2609. }
  2610. break;
  2611. }
  2612. lpParams->numSubKeys = numSubKeys;
  2613. lpParams->RegMaxWait = RegMaxWait;
  2614. lpParams->ConnectArray = ConnectArray;
  2615. lpParams->dwRestoreFlags = dwFlags;
  2616. lpParams->fReconnectFailed = FALSE;
  2617. //
  2618. // Decide whether to show the "Restoring Connections" dialog.
  2619. // In general, if a connection will be made as a user other than the
  2620. // default logged-on user, we need to give the user a chance to enter
  2621. // the password and have it validated, hence we need to contact the
  2622. // server, so we should show the dialog.
  2623. //
  2624. BOOL NeedDialog;
  2625. if (DontDefer)
  2626. {
  2627. NeedDialog = TRUE;
  2628. }
  2629. else
  2630. {
  2631. NeedDialog = FALSE;
  2632. //
  2633. // Get the default user and domain names to connect as
  2634. //
  2635. PUNICODE_STRING UserName;
  2636. PUNICODE_STRING DomainName;
  2637. NTSTATUS ntStatus = LsaGetUserName(&UserName, &DomainName);
  2638. if (NT_SUCCESS(ntStatus))
  2639. {
  2640. MPR_LOG2(RESTORE,"Default domain name = \"%ws\", user name = \"%ws\"\n",
  2641. DomainName->Buffer, UserName->Buffer);
  2642. }
  2643. else
  2644. {
  2645. MPR_LOG(ERROR, "LsaGetUserName failed, %#lx\n", ntStatus);
  2646. DomainName = NULL;
  2647. UserName = NULL;
  2648. }
  2649. //
  2650. // If the logon domain is the local machine, don't defer connections.
  2651. // This is a NT 4.0 workaround for the most common case of bug 36827.
  2652. // When connecting to an LM server, if the user name happens to
  2653. // match a local user name on the target server, the server will
  2654. // normally attempt to log on using THAT user's account; hence we
  2655. // need to prompt for the password. We need to do this since this
  2656. // behavior is by design in the redirector.
  2657. //
  2658. WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH+1];
  2659. DWORD nSize = LENGTH(ComputerName);
  2660. if (DomainName == NULL ||
  2661. GetComputerName(ComputerName, &nSize) == FALSE ||
  2662. _wcsicmp(ComputerName, DomainName->Buffer) == 0)
  2663. {
  2664. MPR_LOG0(RESTORE, "Local logon, will not defer connections\n");
  2665. NeedDialog = TRUE;
  2666. }
  2667. else
  2668. {
  2669. //
  2670. // Prescan the connections to determine which ones we can
  2671. // defer and whether we need the reconnect dialog. If all
  2672. // connections are deferred, we do not bother with the dialog.
  2673. //
  2674. for (DWORD i = 0; i < numSubKeys; i++)
  2675. {
  2676. if (! ConnectArray[i].ContinueFlag)
  2677. {
  2678. continue;
  2679. }
  2680. //
  2681. // The DEFER_UNKNOWN flag means don't defer the connection
  2682. // on this logon, but try the default credentials and see
  2683. // if they work; if so, the connection can be deferred at
  2684. // subsequent logons.
  2685. //
  2686. // Don't defer the connection if a password was explicitly
  2687. // specified when the connection was made. This covers
  2688. // cases in which the redir won't send the default password
  2689. // to the server at connect time because the server doesn't
  2690. // support encrypted passwords.
  2691. //
  2692. if (! (ConnectArray[i].DeferFlags & DEFER_UNKNOWN)
  2693. &&
  2694. ! (ConnectArray[i].DeferFlags & DEFER_EXPLICIT_PASSWORD)
  2695. &&
  2696. MprUserNameMatch(DomainName,
  2697. UserName,
  2698. ConnectArray[i].UserName,
  2699. FALSE))
  2700. {
  2701. //
  2702. // If the user name is the default user name, we can safely
  2703. // replace it with a NULL. (This is not only more optimal, but
  2704. // also required in order to work around a LM redir problem
  2705. // with the way credentials for deferred connections are stored.)
  2706. //
  2707. LocalFree(ConnectArray[i].UserName);
  2708. ConnectArray[i].UserName = NULL;
  2709. //
  2710. // It's OK to defer the connection iff the remembered user
  2711. // name matches the default user name and the provider
  2712. // supports deferred connections.
  2713. //
  2714. if ((ConnectArray[i].dwConnectCaps & WNNC_CON_DEFER)
  2715. &&
  2716. (ConnectArray[i].pfAddConnection3 != NULL))
  2717. {
  2718. //
  2719. // Defer was initialized to 0 when the array was
  2720. // allocated.
  2721. // Note that we don't defer if an lpDevice was supplied.
  2722. //
  2723. ConnectArray[i].Defer = TRUE;
  2724. }
  2725. }
  2726. if (! ConnectArray[i].Defer)
  2727. {
  2728. NeedDialog = TRUE;
  2729. }
  2730. } // for each connection
  2731. MprSortConnectionArray(ConnectArray, numSubKeys);
  2732. }
  2733. if (DomainName != NULL)
  2734. {
  2735. LsaFreeMemory(DomainName->Buffer);
  2736. LsaFreeMemory(DomainName);
  2737. }
  2738. if (UserName != NULL)
  2739. {
  2740. LsaFreeMemory(UserName->Buffer);
  2741. LsaFreeMemory(UserName);
  2742. }
  2743. }
  2744. //
  2745. // If we are:
  2746. // USING DIALOGS WHEN RESTORING CONNECTIONS...
  2747. //
  2748. // This main thread will used to service a windows event loop
  2749. // by calling ShowReconnectDialog. Therefore, a new thread
  2750. // must be created to actually restore the connections. As we
  2751. // attempt to restore each connection, a message is posted in
  2752. // this event loop that causes it to put up a dialog that
  2753. // describes the connection and has a "cancel" option.
  2754. //
  2755. if (! NeedDialog)
  2756. {
  2757. lpParams->hDlg = INVALID_WINDOW_HANDLE;
  2758. // CODEWORK: We are using this INVALID_WINDOW_HANDLE to tell
  2759. // the various routines whether there are 2 threads or not.
  2760. // Instead we should add a new field, BOOL fSeparateThread,
  2761. // to PARAMETERS. This requires changing routines in mprui.dll
  2762. // as well as mpr.dll.
  2763. DoRestoreConnection(lpParams);
  2764. }
  2765. else
  2766. {
  2767. HANDLE hThreadToken;
  2768. // If the main thread is impersonating, we have to copy its token to
  2769. // the new thread that is being spawned so it runs in the correct context
  2770. if (!OpenThreadToken (GetCurrentThread(),
  2771. TOKEN_IMPERSONATE,
  2772. TRUE,
  2773. &hThreadToken)
  2774. &&
  2775. !OpenProcessToken(GetCurrentProcess(),
  2776. TOKEN_IMPERSONATE,
  2777. &hThreadToken))
  2778. {
  2779. //
  2780. // Couldn't open a token on the thread or the process
  2781. //
  2782. status = GetLastError();
  2783. }
  2784. else
  2785. {
  2786. // lpParams->hDlg was initialized to 0 by LocalAlloc
  2787. // and will be set to an HWND by ShowReconnectDialog
  2788. hThread = CreateThread (NULL,
  2789. 0,
  2790. (LPTHREAD_START_ROUTINE) &DoRestoreConnection,
  2791. (LPVOID) lpParams,
  2792. CREATE_SUSPENDED,
  2793. (LPDWORD) &ThreadID);
  2794. if (hThread == NULL)
  2795. {
  2796. status = GetLastError();
  2797. }
  2798. else
  2799. {
  2800. //
  2801. // Make sure the worker thread isn't killed if
  2802. // this thread finishes while it's wedged in a
  2803. // provider call (in MprRestoreThisConnection).
  2804. // The DLL is unloaded by DoRestoreConnection
  2805. //
  2806. lpParams->hDll = LoadLibraryEx( L"mpr.dll",
  2807. NULL,
  2808. LOAD_WITH_ALTERED_SEARCH_PATH );
  2809. if (lpParams->hDll == NULL) {
  2810. MPR_LOG1(RESTORE,
  2811. "LoadLibraryEx for mpr.dll FAILED %d\n",
  2812. GetLastError());
  2813. //
  2814. // This shouldn't happen since all we're
  2815. // doing is upping our refcount
  2816. //
  2817. ASSERT(FALSE);
  2818. }
  2819. //
  2820. // Assign the impersonation token to the
  2821. // thread and resume/start it
  2822. //
  2823. if (!SetThreadToken(&hThread, hThreadToken))
  2824. {
  2825. status = GetLastError();
  2826. TerminateThread(hThread, NO_ERROR);
  2827. CloseHandle(hThread);
  2828. }
  2829. else
  2830. {
  2831. ResumeThread(hThread);
  2832. CloseHandle(hThread);
  2833. status = ShowReconnectDialog(hWnd, lpParams);
  2834. }
  2835. if (status == WN_SUCCESS && lpParams->status != WN_CANCEL &&
  2836. lpParams->status != WN_SUCCESS )
  2837. {
  2838. SetLastError (lpParams->status);
  2839. }
  2840. if (lpParams->status != WN_CANCEL)
  2841. {
  2842. status = MprNotifyErrors (hWnd, ConnectArray, numSubKeys, dwFlags);
  2843. }
  2844. }
  2845. CloseHandle(hThreadToken);
  2846. }
  2847. }
  2848. //
  2849. // Only if we make it to the end of the do-while loop do the thread
  2850. // parameters have to be cleaned up. Since either this thread or
  2851. // the worker thread might exit first (if the user hits Cancel and
  2852. // the worker thread is stuck in a provider call, this thread could
  2853. // exit first), the InterlockedExchange calls here and at the end
  2854. // of DoRestoreConnection ensure that the last thread leaving
  2855. // does the cleanup.
  2856. //
  2857. // NOTE: This relies on the fact that the only break out of
  2858. // this do-while occurs when LocalAlloc for lpParams FAILS.
  2859. // Adding new break statements may break this logic!
  2860. //
  2861. //
  2862. // Case 1: lpParams never gets allocated -- this bit of code
  2863. // never gets hit since we break out of the loop above
  2864. // and nobody cleans up (correctly)
  2865. //
  2866. // Case 2: CreateThread fails -- hThread is NULL, so this thread
  2867. // needs to clean up
  2868. //
  2869. // (CODEWORK -- should this thread call DoRestoreConnection
  2870. // directly in that case?)
  2871. //
  2872. // Case 3: The worker thread exits first -- it changes fDoCleanup
  2873. // to TRUE before it exits and this thread cleans up
  2874. //
  2875. // Case 4: This thread exits first (user cancels dialog) -- this
  2876. // thread changes fDoCleanup to TRUE and orphans the
  2877. // worker thread. When it finishes its provider call,
  2878. // it reads fDoCleanup and cleans up.
  2879. //
  2880. // Poll the worker thread's data to see if reconnect failed.
  2881. if (pfReconnectFailed)
  2882. {
  2883. *pfReconnectFailed = lpParams->fReconnectFailed;
  2884. }
  2885. fDoCleanup = (InterlockedExchange(&lpParams->fDoCleanup, TRUE)
  2886. ||
  2887. hThread == NULL);
  2888. } while (FALSE);
  2889. //
  2890. // Is this thread supposed to clean up? Note that ConnectArray
  2891. // was freed above for the single-drive case so we don't need
  2892. // to worry about it here (i.e., fDoCleanup should only be TRUE
  2893. // in the all-drive, multi-threaded case).
  2894. //
  2895. if (fDoCleanup)
  2896. {
  2897. MPR_LOG0(RESTORE, "Main thread will perform cleanup\n");
  2898. ASSERT(lpParams != NULL);
  2899. //
  2900. // Free up resources in preparation to return.
  2901. //
  2902. if (!CloseHandle(lpParams->hDlgCreated))
  2903. status = GetLastError();
  2904. if (!CloseHandle(lpParams->hDlgFailed))
  2905. status = GetLastError();
  2906. if (!CloseHandle(lpParams->hDonePassword))
  2907. status = GetLastError();
  2908. LocalFree(lpParams);
  2909. MprFreeConnectionArray(ConnectArray, numSubKeys);
  2910. }
  2911. // Send a notification about the new network drive(s).
  2912. if( (g_LUIDDeviceMapsEnabled == TRUE) && (lpDevice != NULL))
  2913. {
  2914. // Use LUID broadcast mechanism
  2915. MprBroadcastDriveChange(
  2916. lpDevice,
  2917. FALSE ); // not a Delete Message
  2918. }
  2919. else
  2920. {
  2921. MprNotifyShell(L" :");
  2922. }
  2923. if (status != WN_SUCCESS)
  2924. {
  2925. SetLastError(status);
  2926. }
  2927. return status;
  2928. }
  2929. VOID
  2930. MprSortConnectionArray(
  2931. LPCONNECTION_INFO lpcConnectArray,
  2932. DWORD dwNumSubKeys
  2933. )
  2934. /*++
  2935. Routine Description:
  2936. This function sorts the array of connections by placing the
  2937. connections that can be deferred before those that can't
  2938. Arguments:
  2939. lpcConnectArray -- The array of CONNECTION_INFO structures
  2940. dwNumSubKeys -- The number of structures in the array
  2941. Notes:
  2942. This sort is done to improve behavior at boot. Deferred connections are
  2943. faster and less error-prone than non-deferred connections and errors can
  2944. cause popups that allow the user to stop reconnecting drives, which leaves
  2945. the remaining drives in the annoying "Unavailable" state. Note that most
  2946. popups for drive reconnection at boot are for credentials, which is a popup
  2947. associated only with non-deferred connection. By reconnecting deferred
  2948. connections first, we avoid this problem.
  2949. --*/
  2950. {
  2951. INT nLow = 0;
  2952. INT nHigh = dwNumSubKeys - 1;
  2953. BOOL fSwap;
  2954. CONNECTION_INFO ciTemp;
  2955. do {
  2956. //
  2957. // Find the first non-deferred connection
  2958. //
  2959. while (nLow < (INT)dwNumSubKeys
  2960. &&
  2961. lpcConnectArray[nLow].Defer) {
  2962. nLow++;
  2963. }
  2964. //
  2965. // Find the last deferred connection
  2966. //
  2967. while (nHigh >= 0
  2968. &&
  2969. !lpcConnectArray[nHigh].Defer) {
  2970. nHigh--;
  2971. }
  2972. fSwap = (nLow < nHigh);
  2973. //
  2974. // Only swap if the pointers haven't crossed
  2975. // (otherwise we'd be undoing the sorting)
  2976. //
  2977. if (fSwap) {
  2978. ciTemp = lpcConnectArray[nLow];
  2979. lpcConnectArray[nLow++] = lpcConnectArray[nHigh];
  2980. lpcConnectArray[nHigh--] = ciTemp;
  2981. }
  2982. }
  2983. while (fSwap);
  2984. #if DBG
  2985. MPR_LOG0(RESTORE, "Order of sorted connection array is as follows:\n");
  2986. for (UINT i = 0; i < dwNumSubKeys; i++) {
  2987. MPR_LOG3(RESTORE,
  2988. "\tLocal: %ws, \tRemote: %ws, \tDefer: %d\n",
  2989. lpcConnectArray[i].NetResource.lpLocalName,
  2990. lpcConnectArray[i].NetResource.lpRemoteName,
  2991. lpcConnectArray[i].Defer);
  2992. }
  2993. #endif // DBG
  2994. }
  2995. VOID
  2996. MprRefcountConnectionArray(
  2997. LPCONNECTION_INFO lpcConnectArray,
  2998. DWORD dwNumSubkeys
  2999. )
  3000. /*++
  3001. Routine Description:
  3002. This function sorts refcounts the provider DLLs in the array
  3003. of connections in preparation for a call outside of mpr.dll
  3004. (which requires releasing the provider lock to avoid the
  3005. possibility of reentrancy and deadlock)
  3006. Arguments:
  3007. lpcConnectArray -- The array of CONNECTION_INFO structures
  3008. dwNumSubKeys -- The number of structures in the array
  3009. Notes:
  3010. --*/
  3011. {
  3012. UINT i;
  3013. LPPROVIDER lpProvider;
  3014. ASSERT_INITIALIZED(NETWORK);
  3015. for (i = 0; i < dwNumSubkeys; i++)
  3016. {
  3017. //
  3018. // If this hits, we're over-refcounting
  3019. //
  3020. ASSERT(lpcConnectArray[i].hProviderDll == NULL);
  3021. lpProvider = &GlobalProviderInfo[lpcConnectArray[i].ProviderIndex];
  3022. lpcConnectArray[i].hProviderDll = LoadLibraryEx(lpProvider->DllName,
  3023. NULL,
  3024. LOAD_WITH_ALTERED_SEARCH_PATH);
  3025. if (lpcConnectArray[i].hProviderDll != NULL)
  3026. {
  3027. lpcConnectArray[i].pfGetCaps = lpProvider->GetCaps;
  3028. lpcConnectArray[i].pfAddConnection3 = lpProvider->AddConnection3;
  3029. lpcConnectArray[i].pfAddConnection = lpProvider->AddConnection;
  3030. lpcConnectArray[i].pfCancelConnection = lpProvider->CancelConnection;
  3031. lpcConnectArray[i].dwConnectCaps = lpProvider->ConnectCaps;
  3032. }
  3033. else
  3034. {
  3035. MPR_LOG2(ERROR,
  3036. "MprRefcountConnectionArray: LoadLibraryEx on %ws failed %d\n",
  3037. lpProvider->DllName,
  3038. GetLastError());
  3039. }
  3040. }
  3041. }
  3042. DWORD
  3043. WNetSetConnectionW(
  3044. IN LPCWSTR lpName,
  3045. IN DWORD dwProperty,
  3046. IN LPVOID pvValue
  3047. )
  3048. /*++
  3049. Routine Description:
  3050. This function changes the characteristics of a network connection.
  3051. Arguments:
  3052. lpName - The name of either the redirected local device or a remote
  3053. network resource.
  3054. dwProperty - Identifies the property to be changed.
  3055. Current properties supported:
  3056. NETPROPERTY_PERSISTENT - pvValue points to a DWORD. If TRUE,
  3057. the connection is made persistent. If FALSE, the connection
  3058. is made non-persistent.
  3059. pvValue - Pointer to the property value. Type depends on dwProperty.
  3060. Return Value:
  3061. WN_SUCCESS - successful
  3062. WN_BAD_LOCALNAME or WN_NOT_CONNECTED - lpName is not a redirected device
  3063. --*/
  3064. {
  3065. DWORD status = WN_SUCCESS;
  3066. if (!(ARGUMENT_PRESENT(lpName) &&
  3067. ARGUMENT_PRESENT(pvValue)))
  3068. {
  3069. SetLastError(WN_BAD_POINTER);
  3070. return(WN_BAD_POINTER);
  3071. }
  3072. // NPGetUser and some Mpr internal functions use lpName as a non-const
  3073. // argument, so we have to make a copy
  3074. WCHAR * lpNameCopy = (WCHAR *) LocalAlloc(LMEM_FIXED, WCSSIZE(lpName));
  3075. if (lpNameCopy == NULL)
  3076. {
  3077. SetLastError(WN_OUT_OF_MEMORY);
  3078. return(WN_OUT_OF_MEMORY);
  3079. }
  3080. wcscpy(lpNameCopy, lpName);
  3081. switch (dwProperty)
  3082. {
  3083. case NETPROPERTY_PERSISTENT:
  3084. {
  3085. MprCheckProviders();
  3086. CProviderSharedLock PLock;
  3087. __try
  3088. {
  3089. //
  3090. // Value should be a boolean DWORD telling whether to
  3091. // remember or forget the connection
  3092. //
  3093. BOOL bRemember = * ((DWORD *) pvValue);
  3094. //
  3095. // Verify that lpName is a redirected local device, and
  3096. // identify the provider responsible
  3097. //
  3098. WCHAR wszRemoteName[MAX_PATH+1];
  3099. DWORD cbBuffer = sizeof(wszRemoteName);
  3100. DWORD iProvider; // provider index
  3101. status = MprGetConnection(
  3102. lpName,
  3103. wszRemoteName,
  3104. &cbBuffer,
  3105. &iProvider);
  3106. if (status == WN_CONNECTION_CLOSED)
  3107. {
  3108. //
  3109. // It's already a remembered connection. Nothing to do.
  3110. //
  3111. status = WN_SUCCESS;
  3112. __leave;
  3113. }
  3114. if (status != WN_SUCCESS)
  3115. {
  3116. ASSERT(status != WN_MORE_DATA);
  3117. __leave;
  3118. }
  3119. if (bRemember)
  3120. {
  3121. //
  3122. // Get the username for the connection from the provider
  3123. //
  3124. WCHAR wszUser[MAX_PATH+1];
  3125. cbBuffer = LENGTH(wszUser);
  3126. status = GlobalProviderInfo[iProvider].GetUser(
  3127. lpNameCopy, wszUser, &cbBuffer);
  3128. if (status != WN_SUCCESS)
  3129. {
  3130. ASSERT(status != WN_MORE_DATA);
  3131. __leave;
  3132. }
  3133. //
  3134. // Get the provider flags, if any
  3135. //
  3136. ASSERT_INITIALIZED(NETWORK);
  3137. BYTE ProviderFlags = 0;
  3138. if (GlobalProviderInfo[iProvider].GetReconnectFlags != NULL)
  3139. {
  3140. // This is an internal entry point so we don't bother with
  3141. // try-except
  3142. DWORD status2 = GlobalProviderInfo[iProvider].GetReconnectFlags(
  3143. lpNameCopy,
  3144. &ProviderFlags
  3145. );
  3146. if (status2 != WN_SUCCESS)
  3147. {
  3148. ProviderFlags = 0;
  3149. }
  3150. MPR_LOG3(RESTORE, "%ws wants flags %#x saved for %ws\n",
  3151. GlobalProviderInfo[iProvider].Resource.lpProvider,
  3152. ProviderFlags,
  3153. lpNameCopy);
  3154. }
  3155. //
  3156. // Make the connection persistent
  3157. //
  3158. status = I_MprSaveConn(
  3159. HKEY_CURRENT_USER,
  3160. GlobalProviderInfo[iProvider].Resource.lpProvider,
  3161. GlobalProviderInfo[iProvider].Type,
  3162. wszUser,
  3163. lpNameCopy,
  3164. wszRemoteName,
  3165. (wcslen(lpName) == 2 && lpName[1] == L':')
  3166. ? RESOURCETYPE_DISK
  3167. : RESOURCETYPE_PRINT,
  3168. ProviderFlags,
  3169. DEFER_UNKNOWN
  3170. );
  3171. }
  3172. else
  3173. {
  3174. //
  3175. // Make the connection non-persistent
  3176. //
  3177. MprForgetRedirConnection(lpNameCopy);
  3178. status = WN_SUCCESS;
  3179. }
  3180. }
  3181. __except (MPR_EXCEPTION_FILTER)
  3182. {
  3183. status = WN_BAD_POINTER;
  3184. }
  3185. break;
  3186. }
  3187. default:
  3188. status = WN_BAD_VALUE;
  3189. }
  3190. LocalFree(lpNameCopy);
  3191. if (status != WN_SUCCESS)
  3192. {
  3193. SetLastError(status);
  3194. }
  3195. return status;
  3196. }
  3197. typedef LRESULT WINAPI FN_PostMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) ;
  3198. #ifdef UNICODE
  3199. #define USER32_DLL_NAME L"user32.dll"
  3200. #define POST_MSG_API_NAME "PostMessageW"
  3201. #else
  3202. #define USER32_DLL_NAME "user32.dll"
  3203. #define POST_MSG_API_NAME "PostMessageA"
  3204. #endif
  3205. static HMODULE _static_hUser32 = NULL;
  3206. static FN_PostMessage * _static_pfPostMsg = NULL;
  3207. VOID
  3208. DoRestoreConnection(
  3209. PARAMETERS *Params
  3210. )
  3211. /*++
  3212. Routine Description:
  3213. This function is run as a separate thread from the main thread (which
  3214. services a windows event loop). It attempts to restore all connections
  3215. that were saved in the registry for this user.
  3216. For each connection that we try to restore, a message is posted to
  3217. the main thread that causes it to put up a dialog box which describes
  3218. the connection and allows the user the option of cancelling.
  3219. If a longer timeout than the default is desired, this function will
  3220. look in the following location in the registry for a timeout value:
  3221. \HKEY_LOCAL_MACHINE\system\CurrentControlSet\Control\NetworkProvider
  3222. RestoreTimeout = REG_DWORD ??
  3223. --*/
  3224. {
  3225. DWORD status = WN_SUCCESS;
  3226. DWORD providerIndex=0;
  3227. DWORD Temp;
  3228. DWORD numSubKeys = Params->numSubKeys;
  3229. DWORD MaxWait = 0; // timeout interval
  3230. DWORD RegMaxWait = Params->RegMaxWait; // timeout interval stored in registry.
  3231. DWORD ElapsedTime; // interval between start and current time.
  3232. DWORD CurrentTime; // Current ClockTick time
  3233. DWORD StartTime;
  3234. DWORD i;
  3235. BOOL UserCancelled = FALSE;
  3236. BOOL ContinueFlag;
  3237. BOOL fDisconnect = FALSE;
  3238. LPCONNECTION_INFO ConnectArray = Params->ConnectArray;
  3239. HANDLE lpHandles[2];
  3240. DWORD dwSleepTime = RECONNECT_SLEEP_INCREMENT ;
  3241. DWORD j;
  3242. DWORD numStillMustContinue;
  3243. DWORD dwValue;
  3244. DWORD RestoredDrivesMask = 0;
  3245. DWORD CurrDriveMask;
  3246. LPWSTR lpLocalName;
  3247. WCHAR DriveLetterName[3]; // "<drive letter>:<NULL>"
  3248. HINSTANCE hDll = Params->hDll; // This needs to be checked
  3249. // Params is freed
  3250. //
  3251. // Don't check providers here as this isn't a top-level WNet API.
  3252. // No need to grab the shared lock here because the ConnectArray
  3253. // already contains all the entrypoints we need and the provider
  3254. // DLLs have all been refcounted.
  3255. //
  3256. lpHandles[0] = Params->hDlgCreated;
  3257. lpHandles[1] = Params->hDlgFailed;
  3258. StartTime = GetTickCount();
  3259. if (RegMaxWait != 0)
  3260. {
  3261. MaxWait = RegMaxWait;
  3262. }
  3263. if ( _static_pfPostMsg == NULL )
  3264. {
  3265. MprEnterLoadLibCritSect();
  3266. if (_static_hUser32 = LoadLibraryEx(USER32_DLL_NAME,
  3267. NULL,
  3268. LOAD_WITH_ALTERED_SEARCH_PATH))
  3269. {
  3270. _static_pfPostMsg = (FN_PostMessage *) GetProcAddress( _static_hUser32,
  3271. POST_MSG_API_NAME );
  3272. }
  3273. MprLeaveLoadLibCritSect();
  3274. if ( _static_pfPostMsg == NULL )
  3275. {
  3276. Params->status = GetLastError();
  3277. goto CleanExit;
  3278. }
  3279. }
  3280. if(Params->hDlg == INVALID_WINDOW_HANDLE)
  3281. {
  3282. dwValue = 0;
  3283. }
  3284. else
  3285. {
  3286. dwValue = WaitForMultipleObjects (2, lpHandles, FALSE, INFINITE);
  3287. }
  3288. switch(dwValue)
  3289. {
  3290. case 1: // hDlgFailed signaled. Info dialog failed to construct.
  3291. break;
  3292. case 0: // hDlgCreated signaled. Info dialog constructed successfully.
  3293. MPR_LOG0(RESTORE,"Enter Loop where we will attempt to restore connections\n");
  3294. do
  3295. {
  3296. //
  3297. // If HideErrors becomes TRUE, stop displaying error dialogs.
  3298. //
  3299. BOOL fHideErrors = (Params->dwRestoreFlags & WNRC_NOUI) ? TRUE : FALSE;
  3300. //
  3301. // Each time we go through all the connections, we need to
  3302. // reset the continue flag to FALSE.
  3303. //
  3304. ContinueFlag = FALSE;
  3305. //
  3306. // Go through each connection information in the array.
  3307. //
  3308. for (i = 0; i < numSubKeys; i++) {
  3309. // Let dialog thread print out the current connection.
  3310. if(Params->hDlg != INVALID_WINDOW_HANDLE)
  3311. {
  3312. (*_static_pfPostMsg) (Params->hDlg,
  3313. SHOW_CONNECTION,
  3314. (WPARAM) ConnectArray[i].NetResource.lpRemoteName,
  3315. 0);
  3316. }
  3317. //
  3318. // Setting status to SUCCESS forces us down the success path
  3319. // if we are not to continue adding this connection.
  3320. //
  3321. status = WN_SUCCESS;
  3322. if (ConnectArray[i].ContinueFlag)
  3323. {
  3324. status = MprRestoreThisConnection (NULL,
  3325. Params,
  3326. &(ConnectArray[i]),
  3327. Params->dwRestoreFlags);
  3328. }
  3329. switch (status)
  3330. {
  3331. case WN_SUCCESS:
  3332. ConnectArray[i].Status = WN_SUCCESS;
  3333. ConnectArray[i].ContinueFlag = FALSE;
  3334. if( g_LUIDDeviceMapsEnabled == TRUE )
  3335. {
  3336. lpLocalName = ConnectArray[i].NetResource.lpLocalName;
  3337. if( lpLocalName != NULL &&
  3338. wcslen(lpLocalName) == 2 &&
  3339. lpLocalName[1] == L':' &&
  3340. iswalpha(lpLocalName[0]) )
  3341. {
  3342. DriveLetterName[0] = RtlUpcaseUnicodeChar( lpLocalName[0] );
  3343. RestoredDrivesMask |= 1<<(DriveLetterName[0] - L'A');
  3344. }
  3345. }
  3346. break;
  3347. case WN_CONTINUE:
  3348. break;
  3349. case WN_NO_NETWORK:
  3350. case WN_FUNCTION_BUSY:
  3351. //
  3352. // If this is the first pass through, we don't have
  3353. // the wait times figured out for each provider. Do that
  3354. // now.
  3355. //
  3356. if (ConnectArray[i].ProviderWait == 0)
  3357. {
  3358. MPR_LOG0(RESTORE,"Ask provider how long it will take "
  3359. "for the net to come up\n");
  3360. Temp = ConnectArray[i].pfGetCaps(WNNC_START);
  3361. MPR_LOG2(RESTORE,"GetCaps(START) for Provider %ws yields %d\n",
  3362. ConnectArray[i].NetResource.lpProvider,
  3363. Temp)
  3364. switch (Temp)
  3365. {
  3366. case PROVIDER_WILL_NOT_START:
  3367. MPR_LOG0(RESTORE,"Provider will not start\n");
  3368. ConnectArray[i].ContinueFlag = FALSE;
  3369. ConnectArray[i].Status = status;
  3370. break;
  3371. case NO_TIME_ESTIMATE:
  3372. MPR_LOG0(RESTORE,"Provider doesn't have time estimate\n");
  3373. if (RegMaxWait != 0) {
  3374. ConnectArray[i].ProviderWait = RegMaxWait;
  3375. }
  3376. else {
  3377. ConnectArray[i].ProviderWait = DEFAULT_WAIT_TIME;
  3378. }
  3379. if (MaxWait < ConnectArray[i].ProviderWait) {
  3380. MaxWait = ConnectArray[i].ProviderWait;
  3381. }
  3382. break;
  3383. default:
  3384. MPR_LOG1(RESTORE,"Provider says it will take %d msec\n",
  3385. Temp);
  3386. if ((Temp <= MAX_ALLOWED_WAIT_TIME) && (Temp > MaxWait))
  3387. {
  3388. MaxWait = Temp;
  3389. }
  3390. ConnectArray[i].ProviderWait = MaxWait;
  3391. break;
  3392. }
  3393. }
  3394. //
  3395. // If the status for this provider has just changed to
  3396. // WN_FUNCTION_BUSY from some other status, then calculate
  3397. // a timeout time by getting the provider's new timeout
  3398. // and adding that to the elapsed time since start. This
  3399. // gives a total elapsed time until timeout - which can
  3400. // be compared with the current MaxWait.
  3401. //
  3402. if ((status == WN_FUNCTION_BUSY) &&
  3403. (ConnectArray[i].Status == WN_NO_NETWORK))
  3404. {
  3405. Temp = ConnectArray[i].pfGetCaps(WNNC_START);
  3406. MPR_LOG2(RESTORE,"Changed from NO_NET to BUSY\n"
  3407. "\tGetCaps(START) for Provider %ws yields %d\n",
  3408. ConnectArray[i].NetResource.lpProvider,
  3409. Temp);
  3410. switch (Temp)
  3411. {
  3412. case PROVIDER_WILL_NOT_START:
  3413. //
  3414. // This is bizzare to find the status = BUSY,
  3415. // but to have the Provider not starting.
  3416. //
  3417. ConnectArray[i].ContinueFlag = FALSE;
  3418. break;
  3419. case NO_TIME_ESTIMATE:
  3420. //
  3421. // No need to alter the timeout for this one.
  3422. //
  3423. break;
  3424. default:
  3425. //
  3426. // Make sure this new timeout information will take
  3427. // less than the maximum allowed time from providers.
  3428. //
  3429. if (Temp <= MAX_ALLOWED_WAIT_TIME)
  3430. {
  3431. CurrentTime = GetTickCount();
  3432. //
  3433. // Determine how much time has elapsed since
  3434. // we started.
  3435. //
  3436. ElapsedTime = CurrentTime - StartTime;
  3437. //
  3438. // Add the Elapsed time to the new timeout we
  3439. // just received from the provider to come up
  3440. // with a timeout value that can be compared
  3441. // with MaxWait.
  3442. //
  3443. Temp += ElapsedTime;
  3444. //
  3445. // If the new timeout is larger that MaxWait,
  3446. // then use the new timeout.
  3447. //
  3448. if (Temp > MaxWait)
  3449. {
  3450. MaxWait = Temp;
  3451. }
  3452. }
  3453. } // End Switch(Temp)
  3454. } // End If (change state from NO_NET to BUSY)
  3455. //
  3456. // Store the status (either NO_NET or BUSY) with the
  3457. // connect info.
  3458. //
  3459. if (ConnectArray[i].ContinueFlag)
  3460. {
  3461. ConnectArray[i].Status = status;
  3462. break;
  3463. }
  3464. case WN_CANCEL:
  3465. ConnectArray[i].Status = status;
  3466. default:
  3467. //
  3468. // For any other error, call the Error Dialog
  3469. //
  3470. Params->fReconnectFailed = TRUE;
  3471. if (fHideErrors) {
  3472. fDisconnect = FALSE;
  3473. } else {
  3474. //
  3475. // Count the number of connections which have not
  3476. // been resolved. If there is exactly one, do not
  3477. // give the user the option to cancel.
  3478. //
  3479. numStillMustContinue = 0;
  3480. for (j = 0; j < numSubKeys; j++) {
  3481. if (ConnectArray[j].ContinueFlag) {
  3482. numStillMustContinue++;
  3483. }
  3484. }
  3485. MPR_LOG1(RESTORE,"DoProfileErrorDialog with "
  3486. "%d connections remaining\n",
  3487. numStillMustContinue);
  3488. //
  3489. // We need to bump up the refcount for mprui.dll
  3490. // here since we're in a separate thread and
  3491. // WNetRestoreConnectionsW could return with this
  3492. // UI still up. If that happens and the process
  3493. // calls WNetClearConnections, we'll AV as soon as
  3494. // a message is sent to the UI window since
  3495. // WNetClearConnections unloads mprui.dll (this can
  3496. // happen in winlogon.exe). Note that this function
  3497. // will load mprui.dll globally if it hasn't already
  3498. // been done so after the first LoadLibrary call here,
  3499. // we're merely playing around with the DLL refcount
  3500. // rather than loading/unloading DLLs every time.
  3501. //
  3502. HINSTANCE hDll = LoadLibraryEx(L"mprui.dll",
  3503. NULL,
  3504. LOAD_WITH_ALTERED_SEARCH_PATH);
  3505. if (hDll == NULL)
  3506. {
  3507. MPR_LOG1(RESTORE,
  3508. "WNetRestoreConnectionW: loading mprui.dll failed %d\n",
  3509. GetLastError());
  3510. fDisconnect = FALSE;
  3511. }
  3512. else
  3513. {
  3514. DoProfileErrorDialog(
  3515. Params->hDlg == INVALID_WINDOW_HANDLE ?
  3516. NULL : Params->hDlg,
  3517. ConnectArray[i].NetResource.lpLocalName,
  3518. ConnectArray[i].NetResource.lpRemoteName,
  3519. ConnectArray[i].NetResource.lpProvider,
  3520. status,
  3521. (Params->hDlg != INVALID_WINDOW_HANDLE) &&
  3522. (numStillMustContinue > 1),
  3523. &UserCancelled,
  3524. &fDisconnect,
  3525. &fHideErrors);
  3526. FreeLibrary(hDll);
  3527. }
  3528. }
  3529. if (fDisconnect)
  3530. {
  3531. if (ConnectArray[i].NetResource.lpLocalName)
  3532. {
  3533. status = ConnectArray[i].pfCancelConnection(
  3534. ConnectArray[i].NetResource.lpLocalName,
  3535. TRUE);
  3536. }
  3537. else
  3538. {
  3539. status =
  3540. MprForgetPrintConnection(
  3541. ConnectArray[i].NetResource.lpRemoteName) ;
  3542. }
  3543. }
  3544. ConnectArray[i].ContinueFlag = FALSE;
  3545. break;
  3546. } // end switch(status)
  3547. ContinueFlag |= ConnectArray[i].ContinueFlag;
  3548. //
  3549. // If the User cancelled all further connection restoration
  3550. // work, then leave this loop.
  3551. //
  3552. if (UserCancelled)
  3553. {
  3554. status = WN_CANCEL;
  3555. ContinueFlag = FALSE;
  3556. break;
  3557. }
  3558. } // end For each connection.
  3559. if (ContinueFlag)
  3560. {
  3561. //
  3562. // Determine what the elapsed time from the start is.
  3563. //
  3564. CurrentTime = GetTickCount();
  3565. ElapsedTime = CurrentTime - StartTime;
  3566. //
  3567. // If a timeout occured, then don't continue. Otherwise, sleep for
  3568. // a bit and loop again through all connections.
  3569. //
  3570. if (ElapsedTime > MaxWait)
  3571. {
  3572. MPR_LOG0(RESTORE,"WNetRestoreConnectionW: Timed out while restoring\n");
  3573. ContinueFlag = FALSE;
  3574. status = WN_SUCCESS;
  3575. }
  3576. else
  3577. {
  3578. Sleep(dwSleepTime);
  3579. //
  3580. // increase sleeptime as we loop, but cap at 4 times
  3581. // the increment (currently that is 4 * 3 secs = 12 secs)
  3582. //
  3583. if (dwSleepTime < (RECONNECT_SLEEP_INCREMENT * 4))
  3584. {
  3585. dwSleepTime += RECONNECT_SLEEP_INCREMENT ;
  3586. }
  3587. }
  3588. }
  3589. } while (ContinueFlag);
  3590. break;
  3591. default:
  3592. status = GetLastError();
  3593. }
  3594. Params->status = status;
  3595. if(Params->hDlg != INVALID_WINDOW_HANDLE)
  3596. {
  3597. (*_static_pfPostMsg) (Params->hDlg, WM_QUIT, 0, 0);
  3598. }
  3599. CleanExit:
  3600. //
  3601. // Is this thread supposed to clean up?
  3602. //
  3603. if (InterlockedExchange(&Params->fDoCleanup, TRUE))
  3604. {
  3605. MPR_LOG0(RESTORE, "Worker thread will perform cleanup\n");
  3606. ASSERT(Params != NULL);
  3607. //
  3608. // Free up resources in preparation to return.
  3609. //
  3610. //
  3611. // If LUID device maps are enabled and we restored drive letter
  3612. // connections, then notify the shell about each restored drive
  3613. // letter connection.
  3614. //
  3615. if( (g_LUIDDeviceMapsEnabled == TRUE) &&
  3616. (RestoredDrivesMask != 0) )
  3617. {
  3618. CurrDriveMask = 1;
  3619. DriveLetterName[1] = L':';
  3620. DriveLetterName[2] = UNICODE_NULL;
  3621. for( DriveLetterName[0] = L'A';
  3622. DriveLetterName[0] <= L'Z';
  3623. DriveLetterName[0]++, CurrDriveMask <<= 1 )
  3624. {
  3625. if( CurrDriveMask & RestoredDrivesMask )
  3626. {
  3627. // Use the LUID broadcast mechanism
  3628. MprBroadcastDriveChange(
  3629. DriveLetterName,
  3630. FALSE ); // not a Delete Message
  3631. }
  3632. }
  3633. }
  3634. if (!CloseHandle(Params->hDlgCreated))
  3635. status = GetLastError();
  3636. if (!CloseHandle(Params->hDlgFailed))
  3637. status = GetLastError();
  3638. if (!CloseHandle(Params->hDonePassword))
  3639. status = GetLastError();
  3640. MprFreeConnectionArray(Params->ConnectArray, numSubKeys);
  3641. LocalFree(Params);
  3642. }
  3643. if (hDll != NULL)
  3644. {
  3645. //
  3646. // We have an HINSTANCE, so we're running in a
  3647. // separate thread
  3648. //
  3649. FreeLibraryAndExitThread(hDll, 0);
  3650. }
  3651. return;
  3652. }
  3653. BOOL
  3654. MprUserNameMatch (
  3655. IN PUNICODE_STRING DomainName,
  3656. IN PUNICODE_STRING UserName,
  3657. IN LPCWSTR RememberedName,
  3658. IN BOOL fMustMatchCompletely
  3659. )
  3660. /*++
  3661. Routine Description:
  3662. This function tests whether the user name for a remembered connection
  3663. matches the default user name.
  3664. Arguments:
  3665. DomainName - default logon domain
  3666. UserName - default logon user
  3667. RememberedName - user name remembered for the connection
  3668. Return Value:
  3669. TRUE if the name matches, FALSE otherwise.
  3670. --*/
  3671. {
  3672. if (IS_EMPTY_STRING(RememberedName))
  3673. {
  3674. return TRUE;
  3675. }
  3676. if (DomainName == NULL || UserName == NULL)
  3677. {
  3678. // This can happen if the LsaGetUserName call fails
  3679. return FALSE;
  3680. }
  3681. //
  3682. // If the remembered name is in the form "domain\user", we must compare
  3683. // against the full user name; otherwise, it's sufficient to compare
  3684. // against the unqualified user name
  3685. //
  3686. WCHAR * pSlash = wcschr(RememberedName, L'\\');
  3687. if (pSlash)
  3688. {
  3689. // Compare user name portion
  3690. UNICODE_STRING RememberedUserName;
  3691. RtlInitUnicodeString(&RememberedUserName, pSlash+1);
  3692. if (! RtlEqualUnicodeString(&RememberedUserName, UserName, TRUE))
  3693. {
  3694. return FALSE;
  3695. }
  3696. // Compare domain name portion
  3697. *pSlash = L'\0';
  3698. UNICODE_STRING RememberedDomainName;
  3699. RtlInitUnicodeString(&RememberedDomainName, RememberedName);
  3700. BOOL fMatch = RtlEqualUnicodeString(&RememberedDomainName, DomainName, TRUE);
  3701. *pSlash = L'\\';
  3702. return fMatch;
  3703. }
  3704. else if (fMustMatchCompletely)
  3705. {
  3706. //
  3707. // A complete match is required but there's no domain in RememberedName
  3708. //
  3709. return FALSE;
  3710. }
  3711. else
  3712. {
  3713. UNICODE_STRING RememberedUserName;
  3714. RtlInitUnicodeString(&RememberedUserName, RememberedName);
  3715. return (RtlEqualUnicodeString(&RememberedUserName, UserName, TRUE));
  3716. }
  3717. }
  3718. DWORD
  3719. MprRestoreThisConnection(
  3720. HWND hWnd,
  3721. PARAMETERS *Params,
  3722. LPCONNECTION_INFO ConnectInfo,
  3723. DWORD dwFlags
  3724. )
  3725. /*++
  3726. Routine Description:
  3727. This function attempts to add a single connection specified in the
  3728. ConnectInfo.
  3729. Arguments:
  3730. Params -
  3731. ConnectInfo - This is a pointer to a connection info structure which
  3732. contains all the information necessary to restore a network
  3733. connection.
  3734. Return Value:
  3735. returns whatever the providers AddConnection function returns.
  3736. --*/
  3737. {
  3738. DWORD status;
  3739. LPTSTR password=NULL;
  3740. HANDLE lpHandle;
  3741. BOOL fDidCancel;
  3742. TCHAR passwordBuffer[PWLEN+1] = {0};
  3743. LPWSTR UserNameForProvider;
  3744. BOOLEAN BufferAllocated = FALSE;
  3745. MPR_LOG3(RESTORE,
  3746. "Doing MprRestoreThisConnection for %ws, username = %ws, defer = %lu...\n",
  3747. ConnectInfo->NetResource.lpRemoteName,
  3748. ConnectInfo->UserName,
  3749. ConnectInfo->Defer);
  3750. //
  3751. // Pass the provider NULL as the user name on the first pass
  3752. // if the original connection used default creds.
  3753. //
  3754. if ( ConnectInfo->DeferFlags & DEFER_DEFAULT_CRED ) {
  3755. UserNameForProvider = NULL;
  3756. } else {
  3757. UserNameForProvider = ConnectInfo->UserName;
  3758. }
  3759. //
  3760. // Loop until we either have a successful connection, or
  3761. // until the user stops attempting to give a proper
  3762. // password.
  3763. //
  3764. do {
  3765. if (Params && Params->status == WN_CANCEL)
  3766. {
  3767. //
  3768. // User cancelled out on reconnections -- return
  3769. // WN_SUCCESS to let DoRestoreConnection finish
  3770. //
  3771. return WN_SUCCESS;
  3772. }
  3773. //
  3774. // Attempt to add the connection.
  3775. // NOTE: The initial password is NULL.
  3776. //
  3777. #if DBG == 1
  3778. DWORD ConnectTime = GetTickCount();
  3779. #endif
  3780. //**************************************
  3781. // Actual call to Provider
  3782. //**************************************
  3783. if (ConnectInfo->Defer)
  3784. {
  3785. ASSERT(ConnectInfo->pfAddConnection3 != NULL);
  3786. status = ConnectInfo->pfAddConnection3(
  3787. NULL, // hwndOwner
  3788. &(ConnectInfo->NetResource), // lpNetResource
  3789. password, // lpPassword
  3790. UserNameForProvider, // lpUserName
  3791. CONNECT_DEFERRED | (ConnectInfo->ProviderFlags << 24)
  3792. ); // dwFlags
  3793. }
  3794. else if (ConnectInfo->pfAddConnection != NULL)
  3795. {
  3796. status = ConnectInfo->pfAddConnection(
  3797. &(ConnectInfo->NetResource), // lpNetResource
  3798. password, // lpPassword
  3799. UserNameForProvider ); // lpUserName
  3800. }
  3801. else
  3802. {
  3803. status = WN_NOT_SUPPORTED;
  3804. }
  3805. #if DBG == 1
  3806. ConnectTime = GetTickCount() - ConnectTime;
  3807. MPR_LOG2(RESTORE, "...provider took %lu ms to return status %lu\n",
  3808. ConnectTime, status);
  3809. if (ConnectInfo->Defer && ConnectTime > 100)
  3810. {
  3811. DbgPrint("[MPR] ------ %ws took %lu ms to restore a DEFERRED\n"
  3812. " connection to %ws !\n",
  3813. ConnectInfo->NetResource.lpProvider, ConnectTime,
  3814. ConnectInfo->NetResource.lpRemoteName);
  3815. }
  3816. #endif
  3817. if (status == WN_CONNECTED_OTHER_PASSWORD_DEFAULT)
  3818. {
  3819. //
  3820. // Provider reconnected using default creds
  3821. //
  3822. status = WN_SUCCESS;
  3823. }
  3824. //
  3825. // If that fails due to a bad password, then
  3826. // loop until we either have a successful connection, or until
  3827. // the user stops attempting to give a proper password.
  3828. //
  3829. if (CREDUI_IS_AUTHENTICATION_ERROR(status))
  3830. {
  3831. //
  3832. // The password needs to be cleared each time around the loop,
  3833. // so that on subsequent add connections, we go back to the
  3834. // logon password.
  3835. //
  3836. password = NULL;
  3837. //
  3838. // If failure was due to bad password, then attempt
  3839. // to get a new password from the user.
  3840. //
  3841. // Changes made by congpay because of another thread.
  3842. if (Params == NULL) //lpDevice != NULL, restoring ONE
  3843. {
  3844. if (!(dwFlags & WNRC_NOUI))
  3845. {
  3846. //
  3847. // We need a username buffer to prompt for a username
  3848. //
  3849. if ( ConnectInfo->UserName == NULL ) {
  3850. ConnectInfo->UserName = (LPTSTR)LocalAlloc(LMEM_FIXED, CRED_MAX_USERNAME_LENGTH * sizeof(WCHAR));
  3851. if ( ConnectInfo->UserName == NULL ) {
  3852. status = ERROR_NOT_ENOUGH_MEMORY;
  3853. SetLastError (status);
  3854. RtlSecureZeroMemory(passwordBuffer, sizeof(passwordBuffer));
  3855. return status;
  3856. } else {
  3857. ConnectInfo->UserName[0] = L'\0';
  3858. BufferAllocated = TRUE;
  3859. }
  3860. }
  3861. //
  3862. // Prompt for a username
  3863. //
  3864. status = DoPasswordDialog(hWnd,
  3865. ConnectInfo->NetResource.lpRemoteName,
  3866. ConnectInfo->UserName,
  3867. passwordBuffer,
  3868. sizeof (passwordBuffer),
  3869. &fDidCancel,
  3870. status);
  3871. //
  3872. // Convert zero length username to NULL buffer
  3873. //
  3874. if ( BufferAllocated && status == NO_ERROR ) {
  3875. //
  3876. // If there is no user name (the length is 0), then set the
  3877. // return pointer to NULL.
  3878. //
  3879. if (STRLEN(ConnectInfo->UserName) == 0) {
  3880. LocalFree(ConnectInfo->UserName);
  3881. ConnectInfo->UserName = NULL;
  3882. BufferAllocated = FALSE;
  3883. }
  3884. }
  3885. }
  3886. if (status != WN_SUCCESS)
  3887. {
  3888. SetLastError (status);
  3889. RtlSecureZeroMemory(passwordBuffer, sizeof(passwordBuffer));
  3890. return status;
  3891. }
  3892. else
  3893. {
  3894. if (fDidCancel)
  3895. {
  3896. status = WN_CANCEL;
  3897. }
  3898. else
  3899. {
  3900. password = passwordBuffer;
  3901. status = WN_ACCESS_DENIED;
  3902. }
  3903. }
  3904. }
  3905. else // restoring all
  3906. {
  3907. if (!(Params->dwRestoreFlags & WNRC_NOUI))
  3908. {
  3909. if (Params->status == WN_CANCEL)
  3910. {
  3911. //
  3912. // User cancelled out of restoring connections. Return
  3913. // WN_SUCCESS to let DoRestoreConnection finish looping
  3914. //
  3915. continue;
  3916. }
  3917. if ( _static_pfPostMsg == NULL ) {
  3918. MprEnterLoadLibCritSect();
  3919. if ( _static_hUser32 = LoadLibraryEx(
  3920. USER32_DLL_NAME,
  3921. NULL,
  3922. LOAD_WITH_ALTERED_SEARCH_PATH ) ) {
  3923. _static_pfPostMsg = (FN_PostMessage *) GetProcAddress( _static_hUser32,
  3924. POST_MSG_API_NAME );
  3925. }
  3926. MprLeaveLoadLibCritSect();
  3927. if ( _static_pfPostMsg == NULL ) {
  3928. RtlSecureZeroMemory(passwordBuffer, sizeof(passwordBuffer));
  3929. return GetLastError();
  3930. }
  3931. }
  3932. lpHandle = Params->hDonePassword;
  3933. Params->pchResource = ConnectInfo->NetResource.lpRemoteName;
  3934. Params->pchUserName = ConnectInfo->UserName;
  3935. Params->dwError = status;
  3936. if ((Params->hDlg == INVALID_WINDOW_HANDLE)
  3937. ||
  3938. ((*_static_pfPostMsg)
  3939. (Params->hDlg,
  3940. DO_PASSWORD_DIALOG,
  3941. (WPARAM) Params,
  3942. 0) == 0))
  3943. {
  3944. //
  3945. // Either we're not using a credential dialog or the
  3946. // PostMessage call failed -- bail.
  3947. //
  3948. MPR_LOG3(ERROR,
  3949. "%ws returned %d for connection to %ws -- bailing\n",
  3950. ConnectInfo->NetResource.lpProvider,
  3951. status,
  3952. ConnectInfo->NetResource.lpRemoteName);
  3953. goto CredentialError;
  3954. }
  3955. WaitForSingleObject ( lpHandle, INFINITE );
  3956. if (Params->status == WN_SUCCESS)
  3957. {
  3958. if (Params->fDidCancel)
  3959. {
  3960. status = WN_CANCEL;
  3961. }
  3962. else
  3963. {
  3964. password = Params->passwordBuffer;
  3965. }
  3966. }
  3967. else
  3968. {
  3969. status = Params->status;
  3970. }
  3971. }
  3972. else
  3973. {
  3974. // Caller wants no UI - don't show any
  3975. status = WN_CANCEL;
  3976. // Remember that we had a failure to reconnect
  3977. Params->fReconnectFailed = TRUE;
  3978. }
  3979. }
  3980. }
  3981. else if (status == WN_SUCCESS)
  3982. {
  3983. DWORD dwIgnoredStatus;
  3984. MPR_LOG1(RESTORE,"MprRestoreThisConnection: Successful "
  3985. "restore of connection for %ws\n",
  3986. ConnectInfo->NetResource.lpRemoteName);
  3987. //
  3988. // Username for the connection might have changed via the
  3989. // password dialog -- update it.
  3990. //
  3991. dwIgnoredStatus = MprSetRegValue(ConnectInfo->RegKey,
  3992. USER_NAME,
  3993. ConnectInfo->UserName,
  3994. 0);
  3995. if (dwIgnoredStatus != ERROR_SUCCESS)
  3996. {
  3997. MPR_LOG(ERROR,
  3998. "MprSetRegValue for user name failed %lu\n",
  3999. dwIgnoredStatus);
  4000. }
  4001. //
  4002. // If the DEFER_UNKNOWN flag was set, we can clear it now.
  4003. // If the default password worked, we know that we can defer the
  4004. // connection in future. If we had to prompt for a password, we
  4005. // can't defer the connection in future.
  4006. // Note: This may not do the right thing for share-level
  4007. // connections where the level of access depends on the password.
  4008. // But we'll let users fix that by manually recreating the
  4009. // connection.
  4010. //
  4011. if (ConnectInfo->DeferFlags & DEFER_UNKNOWN)
  4012. {
  4013. if (password == NULL)
  4014. {
  4015. ConnectInfo->DeferFlags &=
  4016. ~(DEFER_UNKNOWN | DEFER_EXPLICIT_PASSWORD);
  4017. MPR_LOG1(RESTORE,"MprRestoreThisConnection: WILL defer "
  4018. "connection for %ws in future\n",
  4019. ConnectInfo->NetResource.lpRemoteName);
  4020. }
  4021. else
  4022. {
  4023. ConnectInfo->DeferFlags &= ~DEFER_UNKNOWN;
  4024. ConnectInfo->DeferFlags |= DEFER_EXPLICIT_PASSWORD;
  4025. MPR_LOG1(RESTORE,"MprRestoreThisConnection: will NOT defer "
  4026. "connection for %ws in future\n",
  4027. ConnectInfo->NetResource.lpRemoteName);
  4028. }
  4029. dwIgnoredStatus = MprSaveDeferFlags(ConnectInfo->RegKey,
  4030. ConnectInfo->DeferFlags);
  4031. if (dwIgnoredStatus != ERROR_SUCCESS)
  4032. {
  4033. MPR_LOG(ERROR, "MprSaveDeferFlags failed %lu\n", dwIgnoredStatus);
  4034. }
  4035. }
  4036. }
  4037. else
  4038. {
  4039. //
  4040. // An unexpected error occured. In this case,
  4041. // we want to leave the loop.
  4042. //
  4043. MPR_LOG2(ERROR,
  4044. "MprRestoreThisConnection: AddConnection for (%ws) Error %d \n",
  4045. ConnectInfo->NetResource.lpProvider,
  4046. status);
  4047. break;
  4048. }
  4049. //
  4050. // On subsequent iterations,
  4051. // pass the provider the user name typed by the caller.
  4052. //
  4053. UserNameForProvider = ConnectInfo->UserName;
  4054. }
  4055. while (CREDUI_IS_AUTHENTICATION_ERROR(status));
  4056. CredentialError:
  4057. RtlSecureZeroMemory(passwordBuffer, sizeof(passwordBuffer));
  4058. return status;
  4059. }
  4060. DWORD
  4061. MprCreateConnectionArray(
  4062. LPDWORD lpNumConnections,
  4063. LPCTSTR lpDevice,
  4064. LPDWORD lpRegMaxWait,
  4065. LPCONNECTION_INFO *ConnectArray
  4066. )
  4067. /*++
  4068. Routine Description:
  4069. This function creates an array of CONNECTION_INFO structures and fills
  4070. each element in the array with the info that is stored in the registry
  4071. for that connection.
  4072. NOTE: This function allocates memory for the array.
  4073. Arguments:
  4074. NumConnections - This is a pointer to the place where the number of
  4075. connections is to be placed. This indicates how many elements
  4076. are stored in the array.
  4077. lpDevice - If this is NULL, information on all remembered connections
  4078. is required. Otherwise, information for only the lpDevice connection
  4079. is required.
  4080. lpRegMaxWait - This is a pointer to the location where the wait time
  4081. read from the registry is to be placed. If this value does not
  4082. exist in the registry, then the returned value is 0.
  4083. ConnectArray - This is a pointer to the location where the pointer to
  4084. the array is to be placed.
  4085. Return Value:
  4086. An error status code is returned only if something happens that will not
  4087. allow us to restore even one connection.
  4088. --*/
  4089. {
  4090. DWORD status = WN_SUCCESS;
  4091. HKEY connectHandle;
  4092. HKEY providerKeyHandle;
  4093. DWORD maxSubKeyLen;
  4094. DWORD maxValueLen;
  4095. DWORD ValueType;
  4096. DWORD Temp;
  4097. DWORD i;
  4098. BOOL AtLeastOneSuccess = FALSE;
  4099. //
  4100. // init return data
  4101. //
  4102. *lpNumConnections = 0 ;
  4103. *ConnectArray = NULL ;
  4104. //
  4105. // Get a handle for the connection section of the user's registry
  4106. // space.
  4107. //
  4108. if (!MprOpenKey(
  4109. HKEY_CURRENT_USER,
  4110. CONNECTION_KEY_NAME,
  4111. &connectHandle,
  4112. DA_READ)) {
  4113. MPR_LOG(ERROR,"WNetRestoreConnection: MprOpenKey Failed\n",0);
  4114. return(WN_CANNOT_OPEN_PROFILE);
  4115. }
  4116. //
  4117. // Find out the number of connections to restore (numSubKeys) and
  4118. // the max lengths of subkeys and values.
  4119. //
  4120. if(!MprGetKeyInfo(
  4121. connectHandle,
  4122. NULL,
  4123. lpNumConnections,
  4124. &maxSubKeyLen,
  4125. NULL,
  4126. &maxValueLen))
  4127. {
  4128. MPR_LOG(ERROR,"WNetRestoreConnection: MprGetKeyInfo Failed\n",0);
  4129. *lpNumConnections = 0 ;
  4130. RegCloseKey(connectHandle);
  4131. return(WN_CANNOT_OPEN_PROFILE);
  4132. }
  4133. if (*lpNumConnections == 0) {
  4134. RegCloseKey(connectHandle);
  4135. return(WN_SUCCESS);
  4136. }
  4137. if (lpDevice != NULL) {
  4138. *lpNumConnections = 1;
  4139. }
  4140. //
  4141. // Allocate the array.
  4142. //
  4143. *ConnectArray = (LPCONNECTION_INFO)LocalAlloc(
  4144. LPTR,
  4145. *lpNumConnections * sizeof(CONNECTION_INFO));
  4146. if (*ConnectArray == NULL) {
  4147. *lpNumConnections = 0 ;
  4148. RegCloseKey(connectHandle);
  4149. return(GetLastError());
  4150. }
  4151. //
  4152. // Level 1 initialization for the call to MprGetProviderIndex in the loop
  4153. //
  4154. if (!(GlobalInitLevel & FIRST_LEVEL)) {
  4155. status = MprLevel1Init();
  4156. if (status != WN_SUCCESS) {
  4157. RegCloseKey(connectHandle);
  4158. return status;
  4159. }
  4160. }
  4161. for (i=0; i < *lpNumConnections; i++) {
  4162. //
  4163. // Read a Connection Key and accompanying information from the
  4164. // registry.
  4165. //
  4166. // NOTE: If successful, this function will allocate memory for
  4167. // netResource.lpRemoteName,
  4168. // netResource.lpProvider,
  4169. // netResource.lpLocalName, and optionally....
  4170. // userName
  4171. //
  4172. if (!MprReadConnectionInfo(
  4173. connectHandle,
  4174. lpDevice,
  4175. i,
  4176. &((*ConnectArray)[i].ProviderFlags),
  4177. &((*ConnectArray)[i].DeferFlags),
  4178. &((*ConnectArray)[i].UserName),
  4179. &((*ConnectArray)[i].NetResource),
  4180. &((*ConnectArray)[i].RegKey),
  4181. maxSubKeyLen)) {
  4182. //
  4183. // The read failed even though this should be a valid index.
  4184. //
  4185. MPR_LOG0(ERROR,
  4186. "MprCreateConnectionArray: ReadConnectionInfo Failed\n");
  4187. status = WN_CANNOT_OPEN_PROFILE;
  4188. }
  4189. else {
  4190. //
  4191. // Get the Provider Index
  4192. //
  4193. if (MprGetProviderIndex(
  4194. (*ConnectArray)[i].NetResource.lpProvider,
  4195. &((*ConnectArray)[i].ProviderIndex))) {
  4196. AtLeastOneSuccess = TRUE;
  4197. (*ConnectArray)[i].ContinueFlag = TRUE;
  4198. }
  4199. else {
  4200. //
  4201. // The provider index could not be found. This may mean
  4202. // that the provider information stored in the registry
  4203. // is for a provider that is no longer in the ProviderOrder
  4204. // list. (The provider has been removed). In that case,
  4205. // we will just skip this provider. We will leave the
  4206. // ContinueFlag set to 0 (FALSE).
  4207. //
  4208. MPR_LOG0(ERROR,
  4209. "MprCreateConnectionArray:MprGetProviderIndex Failed\n");
  4210. status = WN_BAD_PROVIDER;
  4211. (*ConnectArray)[i].Status = status;
  4212. }
  4213. } // endif (MprReadConnectionInfo)
  4214. } // endfor (i=0; i<numSubKeys)
  4215. if (!AtLeastOneSuccess) {
  4216. //
  4217. // If we gather any connection information, return the last error
  4218. // that occured.
  4219. //
  4220. MprFreeConnectionArray(*ConnectArray,*lpNumConnections);
  4221. *ConnectArray = NULL ;
  4222. *lpNumConnections = 0 ;
  4223. RegCloseKey(connectHandle);
  4224. goto CleanExit;
  4225. }
  4226. RegCloseKey(connectHandle);
  4227. //
  4228. // Read the MaxWait value that is stored in the registry.
  4229. // If it is not there or if the value is less than our default
  4230. // maximum value, then use the default instead.
  4231. //
  4232. if(!MprOpenKey(
  4233. HKEY_LOCAL_MACHINE, // hKey
  4234. NET_PROVIDER_KEY, // lpSubKey
  4235. &providerKeyHandle, // Newly Opened Key Handle
  4236. DA_READ)) { // Desired Access
  4237. MPR_LOG(ERROR,"MprCreateConnectionArray: MprOpenKey (%ws) Error\n",
  4238. NET_PROVIDER_KEY);
  4239. *lpRegMaxWait = 0;
  4240. status = WN_SUCCESS;
  4241. goto CleanExit;
  4242. }
  4243. MPR_LOG(TRACE,"OpenKey %ws\n, ",NET_PROVIDER_KEY);
  4244. Temp = sizeof(*lpRegMaxWait);
  4245. status = RegQueryValueEx(
  4246. providerKeyHandle,
  4247. RESTORE_WAIT_VALUE,
  4248. NULL,
  4249. &ValueType,
  4250. (LPBYTE)lpRegMaxWait,
  4251. &Temp);
  4252. RegCloseKey(providerKeyHandle);
  4253. if (status != NO_ERROR) {
  4254. *lpRegMaxWait = 0;
  4255. }
  4256. status = WN_SUCCESS;
  4257. CleanExit:
  4258. return(WN_SUCCESS);
  4259. }
  4260. VOID
  4261. MprFreeConnectionArray(
  4262. LPCONNECTION_INFO ConnectArray,
  4263. DWORD NumConnections
  4264. )
  4265. /*++
  4266. Routine Description:
  4267. This function frees up all the elements in the connection array, and
  4268. finally frees the array itself.
  4269. Arguments:
  4270. Return Value:
  4271. none
  4272. --*/
  4273. {
  4274. DWORD status = WN_SUCCESS;
  4275. LPNETRESOURCEW netResource;
  4276. DWORD i;
  4277. for (i=0; i<NumConnections; i++)
  4278. {
  4279. netResource = &(ConnectArray[i].NetResource);
  4280. //
  4281. // Free the allocated memory resources.
  4282. //
  4283. LocalFree(netResource->lpLocalName);
  4284. LocalFree(netResource->lpRemoteName);
  4285. LocalFree(netResource->lpProvider);
  4286. LocalFree(ConnectArray[i].UserName);
  4287. if (ConnectArray[i].RegKey != NULL)
  4288. {
  4289. RegCloseKey(ConnectArray[i].RegKey);
  4290. }
  4291. if (ConnectArray[i].hProviderDll != NULL)
  4292. {
  4293. FreeLibrary(ConnectArray[i].hProviderDll);
  4294. }
  4295. }
  4296. LocalFree(ConnectArray);
  4297. return;
  4298. }
  4299. DWORD
  4300. MprNotifyErrors(
  4301. HWND hWnd,
  4302. LPCONNECTION_INFO ConnectArray,
  4303. DWORD NumConnections,
  4304. DWORD dwFlags
  4305. )
  4306. /*++
  4307. Routine Description:
  4308. This function calls the error dialog for each connection that still
  4309. has the continue flag set, and does not have a SUCCESS status.
  4310. Arguments:
  4311. hWnd - This is a window handle that will be used as owner of the
  4312. error dialog.
  4313. ConnectArray - This is the array of connection information.
  4314. At the point when this function is called, the following fields
  4315. are meaningful:
  4316. ContinueFlag - If set, it means that this connection has not yet
  4317. been established.
  4318. StatusFlag - If this is not SUCCESS, then it contains the error
  4319. status from the last call to the provider.
  4320. ContinueFlag Status
  4321. ---------------|---------------
  4322. | FALSE | NotSuccess | Provider will not start
  4323. | FALSE | Success | Connection was successfully established
  4324. | TRUE | NotSuccess | Time-out occured
  4325. | TRUE | Success | This can never occur.
  4326. -------------------------------
  4327. NumConnections - This is the number of entries in the array of
  4328. connection information.
  4329. Return Value:
  4330. --*/
  4331. {
  4332. DWORD i;
  4333. BOOL fDisconnect = FALSE;
  4334. DWORD status = WN_SUCCESS;
  4335. //
  4336. // If HideErrors becomes TRUE, stop displaying error dialogs
  4337. //
  4338. BOOL fHideErrors = (dwFlags & WNRC_NOUI) ? TRUE : FALSE;
  4339. for (i=0; (i<NumConnections) && (!fHideErrors); i++ )
  4340. {
  4341. if ((ConnectArray[i].ContinueFlag) &&
  4342. (ConnectArray[i].Status != WN_SUCCESS) &&
  4343. (ConnectArray[i].Status != WN_CANCEL) &&
  4344. (ConnectArray[i].Status != WN_CONTINUE))
  4345. {
  4346. //
  4347. // For any other error, call the Error Dialog
  4348. //
  4349. DoProfileErrorDialog (
  4350. hWnd,
  4351. ConnectArray[i].NetResource.lpLocalName,
  4352. ConnectArray[i].NetResource.lpRemoteName,
  4353. ConnectArray[i].NetResource.lpProvider,
  4354. ConnectArray[i].Status,
  4355. FALSE, //No cancel button.
  4356. NULL,
  4357. &fDisconnect,
  4358. &fHideErrors);
  4359. if (fDisconnect)
  4360. {
  4361. status = ConnectArray[i].pfCancelConnection(
  4362. ConnectArray[i].NetResource.lpLocalName,
  4363. TRUE);
  4364. }
  4365. }
  4366. }
  4367. return status;
  4368. }
  4369. DWORD
  4370. MprAddPrintersToConnArray(
  4371. LPDWORD lpNumConnections,
  4372. LPCONNECTION_INFO *ConnectArray
  4373. )
  4374. /*++
  4375. Routine Description:
  4376. This function augments the array of CONNECTION_INFO with print connections.
  4377. NOTE: This function allocates memory for the array if need.
  4378. Arguments:
  4379. NumConnections - This is a pointer to the place where the number of
  4380. connections is to be placed. This indicates how many elements
  4381. are stored in the array.
  4382. ConnectArray - This is a pointer to the location where the pointer to
  4383. the array is to be placed.
  4384. Return Value:
  4385. An error status code is returned only if something happens that will not
  4386. allow us to restore even one connection.
  4387. --*/
  4388. {
  4389. DWORD status = WN_SUCCESS;
  4390. HKEY connectHandle;
  4391. DWORD i,j;
  4392. DWORD NumValueNames ;
  4393. DWORD MaxValueNameLength;
  4394. DWORD MaxValueLen ;
  4395. LPNETRESOURCE lpNetResource ;
  4396. LPWSTR lpUserName = NULL ;
  4397. LPWSTR lpProviderName = NULL ;
  4398. LPWSTR lpRemoteName = NULL ;
  4399. LPBYTE lpBuffer = NULL ;
  4400. //
  4401. // Get a handle for the connection section of the user's registry
  4402. // space.
  4403. //
  4404. if (!MprOpenKey(
  4405. HKEY_CURRENT_USER,
  4406. PRINT_CONNECTION_KEY_NAME,
  4407. &connectHandle,
  4408. DA_READ))
  4409. {
  4410. return(WN_SUCCESS); // ignore the restored connections.
  4411. }
  4412. //
  4413. // Find out the number of connections to restore and
  4414. // the max lengths of names and values.
  4415. //
  4416. status = MprGetPrintKeyInfo(connectHandle,
  4417. &NumValueNames,
  4418. &MaxValueNameLength,
  4419. &MaxValueLen) ;
  4420. if (status != WN_SUCCESS || NumValueNames == 0)
  4421. {
  4422. //
  4423. // ignore the restored connections, or nothing to add
  4424. //
  4425. RegCloseKey(connectHandle);
  4426. return(WN_SUCCESS);
  4427. }
  4428. //
  4429. // Allocate the array and copy over the info if previous pointer not null.
  4430. //
  4431. lpBuffer = (LPBYTE) LocalAlloc(LPTR,
  4432. (*lpNumConnections + NumValueNames) *
  4433. sizeof(CONNECTION_INFO)) ;
  4434. if (lpBuffer == NULL)
  4435. {
  4436. RegCloseKey(connectHandle);
  4437. return(GetLastError());
  4438. }
  4439. if (*ConnectArray)
  4440. {
  4441. memcpy(lpBuffer,
  4442. *ConnectArray,
  4443. (*lpNumConnections * sizeof(CONNECTION_INFO))) ;
  4444. LocalFree (*ConnectArray) ;
  4445. }
  4446. //
  4447. // set j to index from previous location, update the count and pointer.
  4448. // then loop thru all new entries, adding to the connect array.
  4449. //
  4450. j = *lpNumConnections ;
  4451. *lpNumConnections += NumValueNames ;
  4452. *ConnectArray = (CONNECTION_INFO *) lpBuffer ;
  4453. //
  4454. // Level 1 initialization for the call to MprGetProviderIndex in the loop
  4455. //
  4456. if (!(GlobalInitLevel & FIRST_LEVEL)) {
  4457. status = MprLevel1Init();
  4458. if (status != WN_SUCCESS) {
  4459. RegCloseKey(connectHandle);
  4460. return status;
  4461. }
  4462. }
  4463. for (i=0; i < NumValueNames; i++, j++)
  4464. {
  4465. DWORD TypeCode ;
  4466. DWORD cbRemoteName = (MaxValueNameLength + 1) * sizeof (WCHAR) ;
  4467. DWORD cbProviderName = MaxValueLen ;
  4468. //
  4469. // allocate the strings for the providername, remotename
  4470. //
  4471. if (!(lpProviderName = (LPWSTR) LocalAlloc(0, cbProviderName )))
  4472. {
  4473. status = GetLastError() ;
  4474. goto ErrorExit ;
  4475. }
  4476. if (!(lpRemoteName = (LPWSTR) LocalAlloc(0, cbRemoteName )))
  4477. {
  4478. status = GetLastError() ;
  4479. goto ErrorExit ;
  4480. }
  4481. //
  4482. // Init the rest. Username currently not set by system, so always NULL
  4483. //
  4484. lpUserName = NULL ;
  4485. lpNetResource = &(*ConnectArray)[j].NetResource ;
  4486. lpNetResource->lpLocalName = NULL ;
  4487. lpNetResource->lpRemoteName = lpRemoteName ;
  4488. lpNetResource->lpProvider = lpProviderName ;
  4489. lpNetResource->dwType = 0 ;
  4490. //
  4491. // null these so we dont free twice if error exit later
  4492. //
  4493. lpRemoteName = NULL ;
  4494. lpProviderName = NULL ;
  4495. status = RegEnumValue(connectHandle,
  4496. i,
  4497. lpNetResource->lpRemoteName,
  4498. &cbRemoteName,
  4499. 0,
  4500. &TypeCode,
  4501. (LPBYTE) lpNetResource->lpProvider,
  4502. &cbProviderName) ;
  4503. if (status == NO_ERROR)
  4504. {
  4505. (*ConnectArray)[j].UserName = lpUserName ;
  4506. //
  4507. // Get the Provider Index
  4508. //
  4509. if (MprGetProviderIndex(
  4510. (*ConnectArray)[j].NetResource.lpProvider,
  4511. &((*ConnectArray)[j].ProviderIndex)))
  4512. {
  4513. (*ConnectArray)[j].ContinueFlag = TRUE;
  4514. }
  4515. else
  4516. {
  4517. //
  4518. // The provider index could not be found. This may mean
  4519. // that the provider information stored in the registry
  4520. // is for a provider that is no longer in the ProviderOrder
  4521. // list. (The provider has been removed). In that case,
  4522. // we will just skip this provider. We will leave the
  4523. // ContinueFlag set to 0 (FALSE).
  4524. //
  4525. status = WN_BAD_PROVIDER;
  4526. (*ConnectArray)[j].Status = status;
  4527. }
  4528. }
  4529. else
  4530. {
  4531. //
  4532. // should not happen, but if it does the array is half built,
  4533. // and cannot be used, so ErrorExit (this will clean it up).
  4534. //
  4535. goto ErrorExit ;
  4536. }
  4537. }
  4538. RegCloseKey(connectHandle);
  4539. return(WN_SUCCESS);
  4540. ErrorExit:
  4541. RegCloseKey(connectHandle);
  4542. LocalFree(lpProviderName) ;
  4543. LocalFree(lpRemoteName) ;
  4544. MprFreeConnectionArray(*ConnectArray,*lpNumConnections);
  4545. *ConnectArray = NULL ;
  4546. *lpNumConnections = 0 ;
  4547. return status;
  4548. }
  4549. VOID
  4550. MprNotifyShell(
  4551. IN LPCWSTR pwszDevice
  4552. )
  4553. /*++
  4554. Routine Description:
  4555. This function sets an event that asks a trusted system component
  4556. (the service controller) to discover the changed network drives and
  4557. asynchronously broadcast a device change message on our behalf.
  4558. CODEWORK: Replace this entire mechanism with real plug-n-play.
  4559. Arguments:
  4560. pwszDevice - Name of the local device (NULL for UNC connections)
  4561. Return Value:
  4562. None
  4563. History:
  4564. BruceFo 19-May-1995 Created, calls BSM directly
  4565. AnirudhS 06-Jun-1996 Set event to have another component do
  4566. the BSM on our behalf
  4567. --*/
  4568. {
  4569. // The shell is only interested in drive redirections
  4570. if (pwszDevice == NULL || wcslen(pwszDevice) != 2 || pwszDevice[1] != L':')
  4571. {
  4572. return;
  4573. }
  4574. // Ask for a device change message to be broadcast
  4575. HANDLE hBSMEvent = OpenEvent(
  4576. EVENT_MODIFY_STATE, // desired access
  4577. FALSE, // don't inherit
  4578. SC_BSM_EVENT_NAME // name
  4579. );
  4580. if (hBSMEvent == NULL)
  4581. {
  4582. MPR_LOG(ERROR, "Couldn't open event for BSM request, %lu\n",
  4583. GetLastError());
  4584. }
  4585. else
  4586. {
  4587. if (! SetEvent(hBSMEvent))
  4588. {
  4589. MPR_LOG(ERROR, "Couldn't set event for BSM request, %lu\n",
  4590. GetLastError());
  4591. }
  4592. CloseHandle(hBSMEvent);
  4593. }
  4594. }
  4595. BOOL
  4596. MprBroadcastDriveChange(
  4597. IN LPCWSTR pwszDevice,
  4598. IN BOOL DeleteMessage
  4599. )
  4600. /*++
  4601. Routine Description:
  4602. This function asynchronously broadcasts a device change message on
  4603. our behalf.
  4604. Arguments:
  4605. pwszDevice - Name of the local device (NULL for UNC connections)
  4606. DeleteMessage - denotes where a delete/add message is needed
  4607. TRUE - send a device deleted message
  4608. FALSE - send a device added message
  4609. Return Value:
  4610. TRUE - Operations completed
  4611. FALSE - Error encountered
  4612. --*/
  4613. {
  4614. BOOL Result;
  4615. DWORD dwFlags = DDD_LUID_BROADCAST_DRIVE;
  4616. // The shell is only interested in drive redirections
  4617. if (pwszDevice == NULL || wcslen(pwszDevice) != 2 || pwszDevice[1] != L':')
  4618. {
  4619. return( FALSE );
  4620. }
  4621. if( DeleteMessage == TRUE )
  4622. {
  4623. dwFlags |= DDD_REMOVE_DEFINITION;
  4624. }
  4625. Result = DefineDosDeviceW( dwFlags, pwszDevice, NULL );
  4626. return( Result );
  4627. }