Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1956 lines
50 KiB

  1. /*
  2. * cnrlink.c - CNRLink ADT module.
  3. */
  4. /* Headers
  5. **********/
  6. #include "project.h"
  7. #pragma hdrstop
  8. #include "cnrlink.h"
  9. #include "server.h"
  10. /* Constants
  11. ************/
  12. /* WNetUseConnection() flag combinations */
  13. #define ALL_CONNECT_IN_FLAGS (CONNECT_UPDATE_PROFILE |\
  14. CONNECT_UPDATE_RECENT |\
  15. CONNECT_TEMPORARY |\
  16. CONNECT_INTERACTIVE |\
  17. CONNECT_PROMPT |\
  18. CONNECT_REDIRECT)
  19. #define ALL_CONNECT_OUT_FLAGS (CONNECT_REFCOUNT |\
  20. CONNECT_LOCALDRIVE)
  21. /* Macros
  22. *********/
  23. /* macros for accessing ICNRLINK data */
  24. #define ICNRL_Remote_Name_PtrA(picnrl) \
  25. ((LPSTR)(((PBYTE)(picnrl)) + (picnrl)->ucbNetNameOffset))
  26. #define ICNRL_Device_PtrA(picnrl) \
  27. ((LPSTR)(((PBYTE)(picnrl)) + (picnrl)->ucbDeviceOffset))
  28. #define ICNRL_Remote_Name_PtrW(picnrl) \
  29. ((LPWSTR)(((PBYTE)(picnrl)) + (picnrl)->ucbNetNameOffsetW))
  30. #define ICNRL_Device_PtrW(picnrl) \
  31. ((LPWSTR)(((PBYTE)(picnrl)) + (picnrl)->ucbDeviceOffsetW))
  32. #define IS_ICNRL_ANSI(picnrl) \
  33. ((PBYTE)(picnrl) + ((PICNRLINKW)(picnrl))->ucbNetNameOffset) == (PBYTE)&(((PICNRLINKW)(picnrl))->ucbNetNameOffsetW)
  34. #ifdef UNICODE
  35. #define ICNRL_Remote_Name_Ptr(picnrl) ICNRL_Remote_Name_PtrW(picnrl)
  36. #define ICNRL_Device_Ptr(picnrl) ICNRL_Device_PtrW(picnrl)
  37. #else
  38. #define ICNRL_Remote_Name_Ptr(picnrl) ICNRL_Remote_Name_PtrA(picnrl)
  39. #define ICNRL_Device_Ptr(picnrl) ICNRL_Device_PtrA(picnrl)
  40. #endif
  41. /* Types
  42. ********/
  43. /*
  44. @doc INTERNAL
  45. @enum ICNRLINKFLAGS | Internal CNRLink structure flags.
  46. */
  47. typedef enum _icnrlinkflags
  48. {
  49. /*
  50. @emem ICNRL_FL_VALID_DEVICE | If set, last redirected drive is valid. If
  51. clear, last redirected drive is not valid.
  52. */
  53. ICNRL_FL_VALID_DEVICE = 0x0001,
  54. /*
  55. @emem ICNRL_FL_VALID_NET_TYPE | If set, net type is valid. If clear, net
  56. type is not valid.
  57. */
  58. ICNRL_FL_VALID_NET_TYPE = 0x0002,
  59. /* @emem ALL_ICNRL_FLAGS | All internal CNRLink structure flags. */
  60. ALL_ICNRL_FLAGS = (ICNRL_FL_VALID_DEVICE |
  61. ICNRL_FL_VALID_NET_TYPE)
  62. }
  63. ICNRLINKFLAGS;
  64. /*
  65. @doc INTERNAL
  66. @struct ICNRLINK | Internal definition of relocatable connectable network
  67. resource (CNR) link structure. An <t ILINKINFO> structure may contain an
  68. ICNRLINK structure. An ICNRLINK structure consists of a header described as
  69. below, followed by variable-length data.
  70. */
  71. typedef struct _icnrlinkA
  72. {
  73. /*
  74. @field UINT | ucbSize | Length of ICNRLINK structure in bytes, including
  75. ucbSize field.
  76. */
  77. UINT ucbSize;
  78. /*
  79. @field DWORD | dwFlags | A bit mask of flags from the <t ICNRLINKFLAGS>
  80. enumeration.
  81. */
  82. DWORD dwFlags;
  83. /*
  84. @field UINT | ucbNetNameOffset | Offset in bytes of CNR name string from
  85. base of structure. The CNR name string may be passed to
  86. WNetUseConnection() to add a connection to the CNR.<nl>
  87. Example CNRLink name string: "\\\\fredbird\\work".
  88. */
  89. UINT ucbNetNameOffset;
  90. /*
  91. @field UINT | ucbDeviceOffset | Offset in bytes of last redirected local
  92. device string from base of structure. This field is only valid if
  93. ICNRL_FL_VALID_DEVICE is set in dwFlags. The last redirected local
  94. device string may be passed to WNetUseConnection() to add a redirected
  95. device connection to the CNR.<nl>
  96. Example last redirected local device string: "D:".
  97. */
  98. UINT ucbDeviceOffset;
  99. /*
  100. @field DWORD | dwNetType | The network type as returned in a
  101. NETINFOSTRUCT. This field is only valid if ICNRL_FL_VALID_NET_TYPE is
  102. set in dwFlags. The net type is used to retrieve the host net resource's
  103. host NP's name to use in calling WNetUseConnection().<nl>
  104. Example net type: WNNC_NET_NETWARE.
  105. */
  106. DWORD dwNetType;
  107. }
  108. ICNRLINKA;
  109. DECLARE_STANDARD_TYPES(ICNRLINKA);
  110. #ifdef UNICODE
  111. typedef struct _icnrlinkW
  112. {
  113. /*
  114. @field UINT | ucbSize | Length of ICNRLINK structure in bytes, including
  115. ucbSize field.
  116. */
  117. UINT ucbSize;
  118. /*
  119. @field DWORD | dwFlags | A bit mask of flags from the <t ICNRLINKFLAGS>
  120. enumeration.
  121. */
  122. DWORD dwFlags;
  123. /*
  124. @field UINT | ucbNetNameOffset | Offset in bytes of CNR name string from
  125. base of structure. The CNR name string may be passed to
  126. WNetUseConnection() to add a connection to the CNR.<nl>
  127. Example CNRLink name string: "\\\\fredbird\\work".
  128. */
  129. UINT ucbNetNameOffset;
  130. /*
  131. @field UINT | ucbDeviceOffset | Offset in bytes of last redirected local
  132. device string from base of structure. This field is only valid if
  133. ICNRL_FL_VALID_DEVICE is set in dwFlags. The last redirected local
  134. device string may be passed to WNetUseConnection() to add a redirected
  135. device connection to the CNR.<nl>
  136. Example last redirected local device string: "D:".
  137. */
  138. UINT ucbDeviceOffset;
  139. /*
  140. @field DWORD | dwNetType | The network type as returned in a
  141. NETINFOSTRUCT. This field is only valid if ICNRL_FL_VALID_NET_TYPE is
  142. set in dwFlags. The net type is used to retrieve the host net resource's
  143. host NP's name to use in calling WNetUseConnection().<nl>
  144. Example net type: WNNC_NET_NETWARE.
  145. */
  146. DWORD dwNetType;
  147. /*
  148. These members are for storing the unicode version of the strings
  149. */
  150. UINT ucbNetNameOffsetW;
  151. UINT ucbDeviceOffsetW;
  152. }
  153. ICNRLINKW;
  154. DECLARE_STANDARD_TYPES(ICNRLINKW);
  155. #endif
  156. #ifdef UNICODE
  157. #define ICNRLINK ICNRLINKW
  158. #define PICNRLINK PICNRLINKW
  159. #define CICNRLINK CICNRLINKW
  160. #define PCICNRLINK PCICNRLINKW
  161. #else
  162. #define ICNRLINK ICNRLINKA
  163. #define PICNRLINK PICNRLINKA
  164. #define CICNRLINK CICNRLINKA
  165. #define PCICNRLINK PCICNRLINKA
  166. #endif
  167. /* Exported from MPR.DLL, but not in winnetwk.h
  168. */
  169. #ifdef UNICODE
  170. DWORD APIENTRY WNetGetResourceInformationW (LPNETRESOURCE lpNetResource, LPVOID lpBuffer, LPDWORD cbBuffer, LPTSTR * lplpSystem);
  171. #define WNetGetResourceInformation WNetGetResourceInformationW
  172. #else
  173. DWORD APIENTRY WNetGetResourceInformationA (LPNETRESOURCE lpNetResource, LPVOID lpBuffer, LPDWORD cbBuffer, LPTSTR * lplpSystem);
  174. #define WNetGetResourceInformation WNetGetResourceInformationA
  175. #endif
  176. /***************************** Private Functions *****************************/
  177. /* Module Prototypes
  178. ********************/
  179. PRIVATE_CODE BOOL GetNetPathFromLocalPath(LPCTSTR, LPTSTR, LPCTSTR *, PBOOL, PDWORD);
  180. PRIVATE_CODE BOOL UnifyICNRLinkInfo(LPCTSTR, DWORD, LPCTSTR, DWORD, PICNRLINK *, PUINT);
  181. PRIVATE_CODE BOOL GetNetType(LPCTSTR, PDWORD);
  182. PRIVATE_CODE BOOL GetNetProviderName(PCICNRLINK, LPTSTR);
  183. PRIVATE_CODE COMPARISONRESULT CompareNetNames(LPCTSTR, LPCTSTR);
  184. PRIVATE_CODE BOOL SearchForRedirectedConnection(PCICNRLINK, LPTSTR);
  185. #if defined(DEBUG) || defined (VSTF)
  186. PRIVATE_CODE BOOL IsValidDevice(LPCTSTR);
  187. PRIVATE_CODE BOOL IsValidNetType(DWORD);
  188. PRIVATE_CODE BOOL IsValidPCICNRLINK(PCICNRLINK);
  189. #endif
  190. #if defined(DEBUG)
  191. PRIVATE_CODE BOOL IsValidNetProviderName(LPCTSTR);
  192. #endif
  193. #if 0
  194. DWORD APIENTRY
  195. WNetGetNetworkInformationW(
  196. LPCWSTR lpProvider,
  197. LPNETINFOSTRUCT lpNetInfoStruct
  198. )
  199. {
  200. if (wcsicmp(lpProvider, L"Microsoft Windows Network") == 0)
  201. {
  202. lpNetInfoStruct->wNetType = (WORD)WNNC_NET_LANMAN;
  203. return ERROR_SUCCESS;
  204. }
  205. else if (wcsicmp(lpProvider, L"Novell Network") == 0)
  206. {
  207. lpNetInfoStruct->wNetType = (WORD)WNNC_NET_NETWARE;
  208. return ERROR_SUCCESS;
  209. }
  210. else
  211. {
  212. return ERROR_NOT_SUPPORTED;
  213. }
  214. }
  215. #endif
  216. /*
  217. ** GetNetPathFromLocalPath()
  218. **
  219. **
  220. **
  221. ** Arguments:
  222. **
  223. ** Returns:
  224. **
  225. ** Side Effects: none
  226. */
  227. PRIVATE_CODE BOOL GetNetPathFromLocalPath(LPCTSTR pcszLocalPath,
  228. LPTSTR pszNetNameBuf,
  229. LPCTSTR *ppcszCommonPathSuffix,
  230. PBOOL pbIsShared, PDWORD pdwNetType)
  231. {
  232. BOOL bResult = TRUE;
  233. PCSERVERVTABLE pcsvt;
  234. ASSERT(IsDrivePath(pcszLocalPath));
  235. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszNetNameBuf, STR, MAX_PATH_LEN));
  236. ASSERT(IS_VALID_WRITE_PTR(ppcszCommonPathSuffix, LPCTSTR));
  237. ASSERT(IS_VALID_WRITE_PTR(pbIsShared, BOOL));
  238. ASSERT(IS_VALID_WRITE_PTR(pdwNetType, DWORD));
  239. *pbIsShared = FALSE;
  240. if (GetServerVTable(&pcsvt))
  241. {
  242. TCHAR rgchSharedPath[MAX_PATH_LEN];
  243. ASSERT(lstrlen(pcszLocalPath) < ARRAYSIZE(rgchSharedPath));
  244. lstrcpy(rgchSharedPath, pcszLocalPath);
  245. FOREVER
  246. {
  247. if ((pcsvt->GetNetResourceFromLocalPath)(rgchSharedPath,
  248. pszNetNameBuf, MAX_PATH_LEN,
  249. pdwNetType))
  250. {
  251. ASSERT(lstrlen(pszNetNameBuf) < MAX_PATH_LEN);
  252. /* Determine common path suffix. */
  253. *ppcszCommonPathSuffix = pcszLocalPath + lstrlen(rgchSharedPath);
  254. /* Skip any leading slash. */
  255. if (IS_SLASH(**ppcszCommonPathSuffix))
  256. *ppcszCommonPathSuffix = CharNext(*ppcszCommonPathSuffix);
  257. ASSERT(! IS_SLASH(**ppcszCommonPathSuffix));
  258. // if it is terminated with a $ it is a hidden share, in that
  259. // case don't consider this shared
  260. *pbIsShared = pszNetNameBuf[lstrlen(pszNetNameBuf) -1] != TEXT('$');
  261. break;
  262. }
  263. else
  264. {
  265. if (! DeleteLastDrivePathElement(rgchSharedPath))
  266. break;
  267. }
  268. }
  269. }
  270. ASSERT(! bResult ||
  271. ! *pbIsShared ||
  272. (EVAL(IsUNCPath(pszNetNameBuf)) &&
  273. IS_VALID_STRING_PTR(*ppcszCommonPathSuffix, CSTR) &&
  274. EVAL(*ppcszCommonPathSuffix >= pcszLocalPath) &&
  275. EVAL(IsStringContained(pcszLocalPath, *ppcszCommonPathSuffix)) &&
  276. EVAL(IsValidNetType(*pdwNetType))));
  277. return(bResult);
  278. }
  279. /*
  280. ** UnifyICNRLinkInfo()
  281. **
  282. **
  283. **
  284. ** Arguments:
  285. **
  286. ** Returns:
  287. **
  288. ** Side Effects: none
  289. */
  290. PRIVATE_CODE BOOL UnifyICNRLinkInfo(LPCTSTR pcszNetName, DWORD dwFlags,
  291. LPCTSTR pcszDevice, DWORD dwNetType,
  292. PICNRLINK *ppicnrl, PUINT pucbICNRLinkLen)
  293. {
  294. BOOL bResult;
  295. UINT ucbDataOffset;
  296. #ifdef UNICODE
  297. BOOL bUnicode;
  298. UINT cchChars;
  299. CHAR szAnsiNetName[MAX_PATH];
  300. CHAR szAnsiDevice[MAX_PATH];
  301. UINT cbAnsiNetName;
  302. UINT cbWideNetName;
  303. UINT cbAnsiDevice;
  304. UINT cbWideDevice;
  305. UINT cbChars;
  306. #endif
  307. ASSERT(IsUNCPath(pcszNetName));
  308. ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_ICNRL_FLAGS));
  309. ASSERT(IS_FLAG_CLEAR(dwFlags, ICNRL_FL_VALID_DEVICE) ||
  310. IsValidDevice(pcszDevice));
  311. ASSERT(IS_FLAG_CLEAR(dwFlags, ICNRL_FL_VALID_NET_TYPE) ||
  312. IsValidNetType(dwNetType));
  313. ASSERT(IS_VALID_WRITE_PTR(ppicnrl, PCNRLINK));
  314. ASSERT(IS_VALID_WRITE_PTR(pucbICNRLinkLen, UINT));
  315. #ifdef UNICODE
  316. bUnicode = FALSE;
  317. cbAnsiNetName = WideCharToMultiByte(CP_ACP, 0,
  318. pcszNetName, -1,
  319. szAnsiNetName, MAX_PATH,
  320. 0, 0);
  321. if ( cbAnsiNetName == 0 )
  322. {
  323. bUnicode = FALSE;
  324. }
  325. else
  326. {
  327. WCHAR szWideNetName[MAX_PATH];
  328. cbChars = MultiByteToWideChar(CP_ACP, 0,
  329. szAnsiNetName, -1,
  330. szWideNetName, MAX_PATH);
  331. if ( cbChars == 0 || lstrcmp(pcszNetName,szWideNetName) != 0 )
  332. {
  333. bUnicode = TRUE;
  334. }
  335. }
  336. if (IS_FLAG_SET(dwFlags, ICNRL_FL_VALID_DEVICE))
  337. {
  338. cbAnsiDevice = WideCharToMultiByte(CP_ACP, 0,
  339. pcszDevice, -1,
  340. szAnsiDevice, MAX_PATH,
  341. 0, 0);
  342. if ( cbAnsiDevice == 0 )
  343. {
  344. bUnicode = FALSE;
  345. }
  346. else
  347. {
  348. WCHAR szWideDevice[MAX_PATH];
  349. cchChars = MultiByteToWideChar(CP_ACP, 0,
  350. szAnsiDevice, -1,
  351. szWideDevice, MAX_PATH);
  352. if ( cchChars == 0 || lstrcmp(pcszDevice,szWideDevice) != 0 )
  353. {
  354. bUnicode = TRUE;
  355. }
  356. }
  357. }
  358. else
  359. {
  360. cbAnsiDevice = 0;
  361. }
  362. if ( bUnicode )
  363. {
  364. ucbDataOffset = SIZEOF(ICNRLINKW);
  365. /* (+ 1) for null terminator. */
  366. cbWideNetName = (lstrlen(pcszNetName) + 1) * sizeof(TCHAR);
  367. if (IS_FLAG_SET(dwFlags, ICNRL_FL_VALID_DEVICE))
  368. cbWideDevice = (lstrlen(pcszDevice) + 1) * sizeof(TCHAR);
  369. else
  370. cbWideDevice = 0;
  371. }
  372. else
  373. {
  374. ucbDataOffset = SIZEOF(ICNRLINKA);
  375. cbWideNetName = 0;
  376. cbWideDevice = 0;
  377. }
  378. *pucbICNRLinkLen = ucbDataOffset +
  379. cbAnsiNetName +
  380. cbAnsiDevice;
  381. if ( bUnicode )
  382. {
  383. *pucbICNRLinkLen = ALIGN_WORD_CNT(*pucbICNRLinkLen) +
  384. cbWideNetName +
  385. cbWideDevice;
  386. }
  387. #else
  388. /* Assume we won't overflow *pucbICNRLinkLen here. */
  389. /* (+ 1) for null terminator. */
  390. *pucbICNRLinkLen = SIZEOF(**ppicnrl) +
  391. (lstrlen(pcszNetName) + 1) * SIZEOF(TCHAR);
  392. if (IS_FLAG_SET(dwFlags, ICNRL_FL_VALID_DEVICE))
  393. /* (+ 1) for null terminator. */
  394. *pucbICNRLinkLen += (lstrlen(pcszDevice) + 1) * SIZEOF(TCHAR);
  395. ucbDataOffset = SIZEOF(ICNRLINKA);
  396. #endif
  397. bResult = AllocateMemory(*pucbICNRLinkLen, ppicnrl);
  398. if (bResult)
  399. {
  400. (*ppicnrl)->ucbSize = *pucbICNRLinkLen;
  401. (*ppicnrl)->dwFlags = dwFlags;
  402. if (IS_FLAG_SET(dwFlags, ICNRL_FL_VALID_NET_TYPE))
  403. (*ppicnrl)->dwNetType = dwNetType;
  404. else
  405. (*ppicnrl)->dwNetType = 0;
  406. /* Append remote name. */
  407. (*ppicnrl)->ucbNetNameOffset = ucbDataOffset;
  408. #ifdef UNICODE
  409. lstrcpyA(ICNRL_Remote_Name_PtrA(*ppicnrl), szAnsiNetName);
  410. ucbDataOffset += cbAnsiNetName;
  411. if (IS_FLAG_SET(dwFlags, ICNRL_FL_VALID_DEVICE))
  412. {
  413. /* Append device name. */
  414. (*ppicnrl)->ucbDeviceOffset = ucbDataOffset;
  415. lstrcpyA(ICNRL_Device_PtrA(*ppicnrl), szAnsiDevice);
  416. ucbDataOffset += cbAnsiDevice;
  417. }
  418. else
  419. {
  420. (*ppicnrl)->ucbDeviceOffset = 0;
  421. }
  422. if ( bUnicode )
  423. {
  424. ucbDataOffset = ALIGN_WORD_CNT(ucbDataOffset);
  425. (*ppicnrl)->ucbNetNameOffsetW = ucbDataOffset;
  426. lstrcpy(ICNRL_Remote_Name_PtrW(*ppicnrl), pcszNetName);
  427. ucbDataOffset += cbWideNetName;
  428. if (IS_FLAG_SET(dwFlags, ICNRL_FL_VALID_DEVICE))
  429. {
  430. /* Append device name. */
  431. (*ppicnrl)->ucbDeviceOffsetW = ucbDataOffset;
  432. lstrcpy(ICNRL_Device_Ptr(*ppicnrl), pcszDevice);
  433. /* (+ 1) for null terminator. */
  434. ucbDataOffset += cbWideDevice;
  435. }
  436. else
  437. {
  438. (*ppicnrl)->ucbDeviceOffsetW = 0;
  439. }
  440. }
  441. #else
  442. lstrcpy(ICNRL_Remote_Name_Ptr(*ppicnrl), pcszNetName);
  443. /* (+ 1) for null terminator. */
  444. ucbDataOffset += lstrlen(pcszNetName) + 1;
  445. if (IS_FLAG_SET(dwFlags, ICNRL_FL_VALID_DEVICE))
  446. {
  447. /* Append device name. */
  448. (*ppicnrl)->ucbDeviceOffset = ucbDataOffset;
  449. lstrcpy(ICNRL_Device_Ptr(*ppicnrl), pcszDevice);
  450. #ifdef DEBUG
  451. /* (+ 1) for null terminator. */
  452. ucbDataOffset += (lstrlen(pcszDevice) + 1) * SIZEOF(TCHAR);
  453. #endif
  454. }
  455. else
  456. (*ppicnrl)->ucbDeviceOffset = 0;
  457. #endif
  458. /* Do all the calculated lengths match? */
  459. ASSERT(ucbDataOffset == (*ppicnrl)->ucbSize);
  460. ASSERT(ucbDataOffset == *pucbICNRLinkLen);
  461. }
  462. ASSERT(! bResult ||
  463. (IS_VALID_STRUCT_PTR(*ppicnrl, CICNRLINK) &&
  464. EVAL(*pucbICNRLinkLen == GetCNRLinkLen((PCCNRLINK)*ppicnrl))));
  465. return(bResult);
  466. }
  467. /*
  468. ** GetNetType()
  469. **
  470. **
  471. **
  472. ** Arguments:
  473. **
  474. ** Returns:
  475. **
  476. ** Side Effects: none
  477. */
  478. PRIVATE_CODE BOOL GetNetType(LPCTSTR pcszCNRName, PDWORD pdwNetType)
  479. {
  480. BOOL bResult = FALSE;
  481. NETRESOURCE nrIn;
  482. NETRESOURCEBUF nrbufOut;
  483. DWORD dwcbBufLen = SIZEOF(nrbufOut);
  484. LPTSTR pszFileSysPath;
  485. DWORD dwNetResult;
  486. #ifdef DEBUG
  487. DWORD dwcmsTicks;
  488. #endif
  489. ASSERT(IsValidCNRName(pcszCNRName));
  490. ASSERT(IS_VALID_WRITE_PTR(pdwNetType, DWORD));
  491. /* RAIDRAID: (15691) We only support disk resource connections here. */
  492. ZeroMemory(&nrIn, SIZEOF(nrIn));
  493. nrIn.lpRemoteName = (LPTSTR)pcszCNRName;
  494. nrIn.dwType = RESOURCETYPE_DISK;
  495. #ifdef DEBUG
  496. dwcmsTicks = GetTickCount();
  497. #endif
  498. dwNetResult = WNetGetResourceInformation(&nrIn, &(nrbufOut.rgbyte),
  499. &dwcbBufLen, &pszFileSysPath);
  500. #ifdef DEBUG
  501. dwcmsTicks = GetTickCount() - dwcmsTicks;
  502. TRACE_OUT((TEXT("GetRemotePathInfo(): WNetGetResourceInformation() on net resource %s took %lu.%03lu seconds."),
  503. pcszCNRName,
  504. (dwcmsTicks / 1000),
  505. (dwcmsTicks % 1000)));
  506. #endif
  507. if (dwNetResult == ERROR_SUCCESS)
  508. {
  509. if (nrbufOut.nr.lpProvider)
  510. {
  511. NETINFOSTRUCT nis;
  512. ASSERT(IS_VALID_STRING_PTR(nrbufOut.nr.lpProvider, STR));
  513. nis.cbStructure = SIZEOF(nis);
  514. dwNetResult = WNetGetNetworkInformation(nrbufOut.nr.lpProvider, &nis);
  515. if (dwNetResult == ERROR_SUCCESS)
  516. {
  517. *pdwNetType = ((nis.wNetType) << 16);
  518. bResult = TRUE;
  519. TRACE_OUT((TEXT("GetNetType(): Net type for CNR %s is %#08lx."),
  520. pcszCNRName,
  521. *pdwNetType));
  522. }
  523. else
  524. WARNING_OUT((TEXT("GetNetType(): WNetGetNetworkInformation() failed for %s NP, returning %lu."),
  525. nrbufOut.nr.lpProvider,
  526. dwNetResult));
  527. }
  528. else
  529. WARNING_OUT((TEXT("GetNetType(): WNetGetResourceInformation() was unable to determine the NP for CNR %s."),
  530. pcszCNRName));
  531. }
  532. else
  533. WARNING_OUT((TEXT("GetNetType(): WNetGetResourceInformation() failed for CNR %s, returning %lu."),
  534. pcszCNRName,
  535. dwNetResult));
  536. ASSERT(! bResult ||
  537. IsValidNetType(*pdwNetType));
  538. return(bResult);
  539. }
  540. /*
  541. ** GetNetProviderName()
  542. **
  543. **
  544. **
  545. ** Arguments:
  546. **
  547. ** Returns:
  548. **
  549. ** Side Effects: none
  550. */
  551. PRIVATE_CODE BOOL GetNetProviderName(PCICNRLINK pcicnrl, LPTSTR pszNPNameBuf)
  552. {
  553. BOOL bResult = FALSE;
  554. ASSERT(IS_VALID_STRUCT_PTR(pcicnrl, CICNRLINK));
  555. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszNPNameBuf, STR, MAX_PATH_LEN));
  556. if (IS_FLAG_SET(pcicnrl->dwFlags, ICNRL_FL_VALID_NET_TYPE))
  557. {
  558. DWORD dwcbNPNameBufLen;
  559. DWORD dwNetResult;
  560. dwcbNPNameBufLen = MAX_PATH_LEN;
  561. dwNetResult = WNetGetProviderName(pcicnrl->dwNetType, pszNPNameBuf,
  562. &dwcbNPNameBufLen);
  563. if (dwNetResult == ERROR_SUCCESS)
  564. {
  565. bResult = TRUE;
  566. #ifdef UNICODE
  567. //
  568. // Unicode builds need to accept both ansi and unicode ICNRLINK structures.
  569. // Note the use of '%S' (upper case). This will accept an ANSI string
  570. // in a UNICODE build environment.
  571. //
  572. if (IS_ICNRL_ANSI(pcicnrl))
  573. TRACE_OUT((TEXT("GetNetProviderName(): NP for CNR %S is %s."),
  574. ICNRL_Remote_Name_PtrA(pcicnrl),
  575. pszNPNameBuf));
  576. else
  577. #endif
  578. TRACE_OUT((TEXT("GetNetProviderName(): NP for CNR %s is %s."),
  579. ICNRL_Remote_Name_Ptr(pcicnrl),
  580. pszNPNameBuf));
  581. }
  582. else
  583. WARNING_OUT((TEXT("GetNetProviderName(): WNetGetProviderName() failed for CNR %s's net type %#08lx, returning %lu."),
  584. TEXT("<Remote Name>"), // ICNRL_Remote_Name_Ptr(pcicnrl),
  585. pcicnrl->dwNetType,
  586. dwNetResult));
  587. }
  588. else
  589. WARNING_OUT((TEXT("GetNetProviderName(): Net type for CNR %s is not known. Unable to determine NP name."),
  590. TEXT("<Remote Name>"))); // ICNRL_Remote_Name_Ptr(pcicnrl)));
  591. ASSERT(! bResult ||
  592. IsValidNetProviderName(pszNPNameBuf));
  593. return(bResult);
  594. }
  595. /*
  596. ** CompareNetNames()
  597. **
  598. **
  599. **
  600. ** Arguments:
  601. **
  602. ** Returns:
  603. **
  604. ** Side Effects: none
  605. */
  606. PRIVATE_CODE COMPARISONRESULT CompareNetNames(LPCTSTR pcszFirstNetName,
  607. LPCTSTR pcszSecondNetName)
  608. {
  609. ASSERT(IS_VALID_STRING_PTR(pcszFirstNetName, CSTR));
  610. ASSERT(IS_VALID_STRING_PTR(pcszSecondNetName, CSTR));
  611. return(MapIntToComparisonResult(lstrcmp(pcszFirstNetName,
  612. pcszSecondNetName)));
  613. }
  614. /*
  615. ** SearchForRedirectedConnection()
  616. **
  617. **
  618. **
  619. ** Arguments:
  620. **
  621. ** Returns:
  622. **
  623. ** Side Effects: none
  624. */
  625. PRIVATE_CODE BOOL SearchForRedirectedConnection(PCICNRLINK pcicnrl,
  626. LPTSTR pszRootPathBuf)
  627. {
  628. BOOL bResult = FALSE;
  629. HANDLE henum;
  630. DWORD dwNetResult;
  631. ASSERT(IS_VALID_STRUCT_PTR(pcicnrl, CICNRLINK));
  632. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszRootPathBuf, STR, MAX_PATH_LEN));
  633. #ifdef DEBUG
  634. #ifdef UNICODE
  635. {
  636. LPWSTR pszWideNetName;
  637. WCHAR szWideNetName[MAX_PATH];
  638. if (IS_ICNRL_ANSI(pcicnrl))
  639. {
  640. pszWideNetName = szWideNetName;
  641. MultiByteToWideChar(CP_ACP, 0,
  642. ICNRL_Remote_Name_PtrA(pcicnrl), -1,
  643. szWideNetName, MAX_PATH);
  644. } else {
  645. pszWideNetName = ICNRL_Remote_Name_PtrW(pcicnrl);
  646. }
  647. WARNING_OUT((TEXT("SearchForRedirectedConnection(): Enumerating local connections searching for redirected connection to CNR \"%s\"."),
  648. pszWideNetName));
  649. }
  650. #else
  651. WARNING_OUT((TEXT("SearchForRedirectedConnection(): Enumerating local connections searching for redirected connection to CNR \"%s\"."),
  652. ICNRL_Remote_Name_Ptr(pcicnrl)));
  653. #endif
  654. #endif
  655. /* RAIDRAID: (15691) We only support container resources here. */
  656. dwNetResult = WNetOpenEnum(RESOURCE_CONNECTED, RESOURCETYPE_DISK,
  657. RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED,
  658. NULL, &henum);
  659. if (dwNetResult == WN_SUCCESS)
  660. {
  661. DWORD dwc = 1;
  662. NETRESOURCEBUF nrbuf;
  663. DWORD dwcbBufLen = SIZEOF(nrbuf);
  664. while ((dwNetResult = WNetEnumResource(henum, &dwc, &(nrbuf.rgbyte),
  665. &dwcbBufLen))
  666. == WN_SUCCESS)
  667. {
  668. /* Is this a redirected connection? */
  669. if (nrbuf.nr.lpRemoteName != NULL)
  670. {
  671. if (nrbuf.nr.lpLocalName != NULL)
  672. {
  673. /* Yes. Is it a redirected connection to the desired CNR? */
  674. #ifdef UNICODE
  675. WCHAR szWideNetName[MAX_PATH];
  676. LPWSTR pszWideNetName;
  677. if (IS_ICNRL_ANSI(pcicnrl))
  678. {
  679. pszWideNetName = szWideNetName;
  680. MultiByteToWideChar(CP_ACP, 0,
  681. ICNRL_Remote_Name_PtrA(pcicnrl), -1,
  682. szWideNetName, MAX_PATH);
  683. }
  684. else
  685. {
  686. pszWideNetName = ICNRL_Remote_Name_Ptr(pcicnrl);
  687. }
  688. if (CompareNetNames(pszWideNetName,
  689. nrbuf.nr.lpRemoteName)
  690. == CR_EQUAL)
  691. #else
  692. if (CompareNetNames(ICNRL_Remote_Name_Ptr(pcicnrl),
  693. nrbuf.nr.lpRemoteName)
  694. == CR_EQUAL)
  695. #endif
  696. {
  697. /* Yes. */
  698. ASSERT(lstrlen(nrbuf.nr.lpLocalName) < MAX_PATH_LEN);
  699. lstrcpy(pszRootPathBuf, nrbuf.nr.lpLocalName);
  700. bResult = TRUE;
  701. TRACE_OUT((TEXT("SearchForRedirectedConnection(): Found CNR \"%s\" connected to %s."),
  702. nrbuf.nr.lpRemoteName,
  703. pszRootPathBuf));
  704. break;
  705. }
  706. else
  707. /* No. */
  708. TRACE_OUT((TEXT("SearchForRedirectedConnection(): Skipping unmatched enumerated connection to CNR \"%s\" on %s."),
  709. nrbuf.nr.lpRemoteName,
  710. nrbuf.nr.lpLocalName));
  711. }
  712. else
  713. /* No. */
  714. TRACE_OUT((TEXT("SearchForRedirectedConnection(): Skipping enumerated deviceless connection to CNR \"%s\"."),
  715. nrbuf.nr.lpRemoteName));
  716. }
  717. else
  718. WARNING_OUT((TEXT("SearchForRedirectedConnection(): Skipping enumerated connection with no CNR name.")));
  719. }
  720. if (! bResult && dwNetResult != WN_NO_MORE_ENTRIES)
  721. WARNING_OUT((TEXT("SearchForRedirectedConnection(): WNetEnumResource() failed, returning %lu."),
  722. dwNetResult));
  723. dwNetResult = WNetCloseEnum(henum);
  724. if (dwNetResult != WN_SUCCESS)
  725. WARNING_OUT((TEXT("SearchForRedirectedConnection(): WNetCloseEnum() failed, returning %lu."),
  726. dwNetResult));
  727. }
  728. else
  729. WARNING_OUT((TEXT("SearchForRedirectedConnection(): WNetOpenEnum() failed, returning %lu."),
  730. dwNetResult));
  731. return(bResult);
  732. }
  733. #if defined(DEBUG) || defined (VSTF)
  734. /*
  735. ** IsValidDevice()
  736. **
  737. **
  738. **
  739. ** Arguments:
  740. **
  741. ** Returns:
  742. **
  743. ** Side Effects: none
  744. */
  745. PRIVATE_CODE BOOL IsValidDevice(LPCTSTR pcszDevice)
  746. {
  747. /* Any valid string < MAX_PATH_LEN bytes long is a valid device name. */
  748. return(IS_VALID_STRING_PTR(pcszDevice, CSTR) &&
  749. EVAL(lstrlen(pcszDevice) < MAX_PATH_LEN));
  750. }
  751. /*
  752. ** IsValidNetType()
  753. **
  754. **
  755. **
  756. ** Arguments:
  757. **
  758. ** Returns:
  759. **
  760. ** Side Effects: none
  761. */
  762. PRIVATE_CODE BOOL IsValidNetType(DWORD dwNetType)
  763. {
  764. BOOL bResult;
  765. switch (dwNetType & 0xffff0000)
  766. {
  767. default:
  768. WARNING_OUT((TEXT("IsValidNetType(): Unexpected net type %#08lx is neither NetWare nor LANMan."),
  769. dwNetType));
  770. /* Fall through... */
  771. case WNNC_NET_LANMAN:
  772. case WNNC_NET_NETWARE:
  773. bResult = TRUE;
  774. break;
  775. }
  776. if (dwNetType & 0x0000ffff)
  777. WARNING_OUT((TEXT("IsValidNetType(): Low word of net type %#08lx is non-zero."),
  778. dwNetType));
  779. return(bResult);
  780. }
  781. /*
  782. ** IsValidPCICNRLINK()
  783. **
  784. **
  785. **
  786. ** Arguments:
  787. **
  788. ** Returns:
  789. **
  790. ** Side Effects: none
  791. */
  792. PRIVATE_CODE BOOL IsValidPCICNRLINK(PCICNRLINK pcicnrl)
  793. {
  794. BOOL bResult;
  795. if (IS_VALID_READ_PTR(pcicnrl, CICNRLINK) &&
  796. IS_VALID_READ_BUFFER_PTR(pcicnrl, CICNRLINK, pcicnrl->ucbSize) &&
  797. FLAGS_ARE_VALID(pcicnrl->dwFlags, ALL_ICNRL_FLAGS) &&
  798. EVAL(IsValidCNRName(ICNRL_Remote_Name_Ptr(pcicnrl))) &&
  799. EVAL(IsContained(pcicnrl, pcicnrl->ucbSize,
  800. ICNRL_Remote_Name_PtrA(pcicnrl),
  801. lstrlenA(ICNRL_Remote_Name_PtrA(pcicnrl)))) &&
  802. (IS_FLAG_CLEAR(pcicnrl->dwFlags, ICNRL_FL_VALID_NET_TYPE) ||
  803. EVAL(IsValidNetType(pcicnrl->dwNetType))))
  804. {
  805. if (IS_FLAG_CLEAR(pcicnrl->dwFlags, ICNRL_FL_VALID_DEVICE))
  806. {
  807. ASSERT(! pcicnrl->ucbDeviceOffset);
  808. bResult = TRUE;
  809. }
  810. else
  811. bResult = (EVAL(IsValidDevice(ICNRL_Device_Ptr(pcicnrl))) &&
  812. EVAL(IsContained(pcicnrl, pcicnrl->ucbSize,
  813. ICNRL_Device_PtrA(pcicnrl),
  814. lstrlenA(ICNRL_Device_PtrA(pcicnrl)))));
  815. }
  816. else
  817. bResult = FALSE;
  818. return(bResult);
  819. }
  820. #endif
  821. #if defined(DEBUG)
  822. /*
  823. ** IsValidNetProviderName()
  824. **
  825. **
  826. **
  827. ** Arguments:
  828. **
  829. ** Returns:
  830. **
  831. ** Side Effects: none
  832. */
  833. PRIVATE_CODE BOOL IsValidNetProviderName(LPCTSTR pcszNetProvider)
  834. {
  835. /* Any string < MAX_PATH_LEN characters long is a valid NP name. */
  836. return(IS_VALID_STRING_PTR(pcszNetProvider, CSTR) &&
  837. lstrlen(pcszNetProvider) < MAX_PATH_LEN);
  838. }
  839. #endif
  840. /****************************** Public Functions *****************************/
  841. /*
  842. ** CreateLocalCNRLink()
  843. **
  844. **
  845. **
  846. ** Arguments:
  847. **
  848. ** Returns:
  849. **
  850. ** Side Effects: none
  851. **
  852. ** If TRUE is returned:
  853. ** 1) *ppcnrl is only valid if *pucbCNRLinkLen > 0.
  854. ** 2) pszLocalBasePathBuf is valid.
  855. ** 3) *ppcszCommonPathSuffix is valid.
  856. **
  857. ** If *pucbCNRLinkLen == 0, pszLocalBasePathBuf is a copy of pcszLocalPath, and
  858. ** *ppcszCommonPathSuffix points at the null terminator of pcszLocalPath.
  859. **
  860. ** If *pucbCNRLinkLen > 0, pszLocalBasePathBuf is the closest shared local base
  861. ** path, and *ppcszCommonPathSuffix points at that path's suffix in
  862. ** pcszLocalPath.
  863. */
  864. PUBLIC_CODE BOOL CreateLocalCNRLink(LPCTSTR pcszLocalPath, PCNRLINK *ppcnrl,
  865. PUINT pucbCNRLinkLen,
  866. LPTSTR pszLocalBasePathBuf,
  867. LPCTSTR *ppcszCommonPathSuffix)
  868. {
  869. BOOL bResult;
  870. TCHAR rgchNetName[MAX_PATH_LEN];
  871. BOOL bShared;
  872. DWORD dwNetType;
  873. ASSERT(IsDrivePath(pcszLocalPath));
  874. ASSERT(IS_VALID_WRITE_PTR(ppcnrl, PCNRLINK));
  875. ASSERT(IS_VALID_WRITE_PTR(pucbCNRLinkLen, UINT));
  876. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszLocalBasePathBuf, STR, MAX_PATH_LEN));
  877. ASSERT(IS_VALID_WRITE_PTR(ppcszCommonPathSuffix, LPCTSTR));
  878. bResult = GetNetPathFromLocalPath(pcszLocalPath, rgchNetName,
  879. ppcszCommonPathSuffix, &bShared,
  880. &dwNetType);
  881. if (bResult)
  882. {
  883. if (bShared)
  884. {
  885. bResult = UnifyICNRLinkInfo(rgchNetName, ICNRL_FL_VALID_NET_TYPE,
  886. NULL, dwNetType, (PICNRLINK *)ppcnrl,
  887. pucbCNRLinkLen);
  888. if (bResult)
  889. {
  890. UINT ucbLocalBasePathLen;
  891. /* Copy local base path into output buffer. */
  892. ASSERT(*ppcszCommonPathSuffix >= pcszLocalPath);
  893. ucbLocalBasePathLen = (UINT)(*ppcszCommonPathSuffix - pcszLocalPath);
  894. CopyMemory(pszLocalBasePathBuf, pcszLocalPath, ucbLocalBasePathLen * sizeof(TCHAR));
  895. pszLocalBasePathBuf[ucbLocalBasePathLen] = TEXT('\0');
  896. }
  897. }
  898. else
  899. {
  900. /* Not shared. No CNRLink. */
  901. *pucbCNRLinkLen = 0;
  902. /* Copy entire local path into output buffer. */
  903. lstrcpy(pszLocalBasePathBuf, pcszLocalPath);
  904. /* Common path suffix is the empty string. */
  905. *ppcszCommonPathSuffix = pcszLocalPath + lstrlen(pcszLocalPath);
  906. }
  907. }
  908. ASSERT(! bResult ||
  909. (EVAL(IsDrivePath(pszLocalBasePathBuf)) &&
  910. IS_VALID_STRING_PTR(*ppcszCommonPathSuffix, CSTR) &&
  911. EVAL(IsStringContained(pcszLocalPath, *ppcszCommonPathSuffix)) &&
  912. (! *pucbCNRLinkLen ||
  913. (IS_VALID_STRUCT_PTR((PCICNRLINK)*ppcnrl, CICNRLINK) &&
  914. EVAL(*pucbCNRLinkLen == GetCNRLinkLen(*ppcnrl))))));
  915. return(bResult);
  916. }
  917. /*
  918. ** CreateRemoteCNRLink()
  919. **
  920. **
  921. **
  922. ** Arguments:
  923. **
  924. ** Returns:
  925. **
  926. ** Side Effects: none
  927. */
  928. PUBLIC_CODE BOOL CreateRemoteCNRLink(LPCTSTR pcszRemotePath, LPCTSTR pcszCNRName,
  929. PCNRLINK *ppcnrl, PUINT pucbCNRLinkLen)
  930. {
  931. BOOL bResult;
  932. /* "D:" + null terminator. */
  933. TCHAR rgchDrive[3];
  934. DWORD dwNetType;
  935. ASSERT(IsCanonicalPath(pcszRemotePath));
  936. ASSERT(IsValidCNRName(pcszCNRName));
  937. ASSERT(IS_VALID_WRITE_PTR(ppcnrl, PCNRLINK));
  938. ASSERT(IS_VALID_WRITE_PTR(pucbCNRLinkLen, UINT));
  939. /* Determine net provider. */
  940. bResult = GetNetType(pcszCNRName, &dwNetType);
  941. if (bResult)
  942. {
  943. DWORD dwFlags = ICNRL_FL_VALID_NET_TYPE;
  944. /* Determine last redirected drive, if any. */
  945. if (IsDrivePath(pcszRemotePath))
  946. {
  947. MyLStrCpyN(rgchDrive, pcszRemotePath, ARRAYSIZE(rgchDrive));
  948. SET_FLAG(dwFlags, ICNRL_FL_VALID_DEVICE);
  949. }
  950. else
  951. rgchDrive[0] = TEXT('\0');
  952. bResult = UnifyICNRLinkInfo(pcszCNRName, dwFlags, rgchDrive, dwNetType,
  953. (PICNRLINK *)ppcnrl, pucbCNRLinkLen);
  954. }
  955. ASSERT(! bResult ||
  956. (IS_VALID_STRUCT_PTR((PCICNRLINK)*ppcnrl, CICNRLINK) &&
  957. EVAL(*pucbCNRLinkLen == GetCNRLinkLen(*ppcnrl))));
  958. return(bResult);
  959. }
  960. /*
  961. ** DestroyCNRLink()
  962. **
  963. **
  964. **
  965. ** Arguments:
  966. **
  967. ** Returns:
  968. **
  969. ** Side Effects: none
  970. */
  971. PUBLIC_CODE void DestroyCNRLink(PCNRLINK pcnrl)
  972. {
  973. ASSERT(IS_VALID_STRUCT_PTR(pcnrl, CCNRLINK));
  974. FreeMemory(pcnrl);
  975. return;
  976. }
  977. /*
  978. ** CompareCNRLinks()
  979. **
  980. **
  981. **
  982. ** Arguments:
  983. **
  984. ** Returns:
  985. **
  986. ** Side Effects: none
  987. **
  988. ** CNR link data is compared in the following order:
  989. **
  990. ** 1) net name
  991. **
  992. ** N.b., net types are ignored when comparing CNRLinks.
  993. */
  994. PUBLIC_CODE COMPARISONRESULT CompareCNRLinks(PCCNRLINK pccnrlFirst,
  995. PCCNRLINK pccnrlSecond)
  996. {
  997. #ifdef UNICODE
  998. WCHAR szWideNetNameFirst[MAX_PATH];
  999. LPWSTR pszWideNetNameFirst;
  1000. WCHAR szWideNetNameSecond[MAX_PATH];
  1001. LPWSTR pszWideNetNameSecond;
  1002. #endif
  1003. ASSERT(IS_VALID_STRUCT_PTR(pccnrlFirst, CCNRLINK));
  1004. ASSERT(IS_VALID_STRUCT_PTR(pccnrlSecond, CCNRLINK));
  1005. #ifdef UNICODE
  1006. if (IS_ICNRL_ANSI(pccnrlFirst))
  1007. {
  1008. pszWideNetNameFirst = szWideNetNameFirst;
  1009. MultiByteToWideChar(CP_ACP, 0,
  1010. ICNRL_Remote_Name_PtrA((PCICNRLINK)pccnrlFirst), -1,
  1011. szWideNetNameFirst, MAX_PATH);
  1012. }
  1013. else
  1014. {
  1015. pszWideNetNameFirst = ICNRL_Remote_Name_Ptr((PCICNRLINK)pccnrlFirst);
  1016. }
  1017. if (IS_ICNRL_ANSI(pccnrlSecond))
  1018. {
  1019. pszWideNetNameSecond = szWideNetNameSecond;
  1020. MultiByteToWideChar(CP_ACP, 0,
  1021. ICNRL_Remote_Name_PtrA((PCICNRLINK)pccnrlSecond), -1,
  1022. szWideNetNameSecond, MAX_PATH);
  1023. }
  1024. else
  1025. {
  1026. pszWideNetNameSecond = ICNRL_Remote_Name_Ptr((PCICNRLINK)pccnrlSecond);
  1027. }
  1028. return(CompareNetNames(pszWideNetNameFirst,pszWideNetNameSecond));
  1029. #else
  1030. return(CompareNetNames(ICNRL_Remote_Name_Ptr((PCICNRLINK)pccnrlFirst),
  1031. ICNRL_Remote_Name_Ptr((PCICNRLINK)pccnrlSecond)));
  1032. #endif
  1033. }
  1034. /*
  1035. ** GetLocalPathFromCNRLink()
  1036. **
  1037. **
  1038. **
  1039. ** Arguments:
  1040. **
  1041. ** Returns:
  1042. **
  1043. ** Side Effects: none
  1044. */
  1045. PUBLIC_CODE BOOL GetLocalPathFromCNRLink(PCCNRLINK pccnrl,
  1046. LPTSTR pszLocalPathBuf,
  1047. PDWORD pdwOutFlags)
  1048. {
  1049. BOOL bResult;
  1050. PCSERVERVTABLE pcsvt;
  1051. ASSERT(IS_VALID_STRUCT_PTR(pccnrl, CCNRLINK));
  1052. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszLocalPathBuf, STR, MAX_PATH_LEN));
  1053. ASSERT(IS_VALID_WRITE_PTR(pdwOutFlags, DWORD));
  1054. *pdwOutFlags = 0;
  1055. bResult = GetServerVTable(&pcsvt);
  1056. if (bResult)
  1057. {
  1058. DWORD dwNetType;
  1059. BOOL bIsLocal;
  1060. /*
  1061. * Get local path for share. N.b., the share name must be in upper case
  1062. * here for MSSHRUI.DLL.
  1063. */
  1064. dwNetType = (IS_FLAG_SET(((PCICNRLINK)pccnrl)->dwFlags,
  1065. ICNRL_FL_VALID_NET_TYPE) ?
  1066. ((PCICNRLINK)pccnrl)->dwNetType :
  1067. 0);
  1068. #ifdef UNICODE
  1069. {
  1070. WCHAR szWideNetName[MAX_PATH];
  1071. LPWSTR pszWideNetName = szWideNetName;
  1072. if (IS_ICNRL_ANSI(pccnrl))
  1073. {
  1074. MultiByteToWideChar(CP_ACP, 0,
  1075. ICNRL_Remote_Name_PtrA((PCICNRLINK)pccnrl), -1,
  1076. szWideNetName, MAX_PATH);
  1077. }
  1078. else
  1079. {
  1080. pszWideNetName = ICNRL_Remote_Name_Ptr((PCICNRLINK)pccnrl);
  1081. }
  1082. bResult = (pcsvt->GetLocalPathFromNetResource)(
  1083. pszWideNetName, dwNetType,
  1084. pszLocalPathBuf, MAX_PATH_LEN, &bIsLocal);
  1085. }
  1086. #else
  1087. bResult = (pcsvt->GetLocalPathFromNetResource)(
  1088. ICNRL_Remote_Name_Ptr((PCICNRLINK)pccnrl), dwNetType,
  1089. pszLocalPathBuf, MAX_PATH_LEN, &bIsLocal);
  1090. #endif
  1091. if (bIsLocal)
  1092. SET_FLAG(*pdwOutFlags, CNR_FL_LOCAL);
  1093. }
  1094. ASSERT(FLAGS_ARE_VALID(*pdwOutFlags, ALL_CNR_FLAGS) &&
  1095. (! bResult ||
  1096. (EVAL(IS_FLAG_SET(*pdwOutFlags, CNR_FL_LOCAL)) &&
  1097. EVAL(IsLocalDrivePath(pszLocalPathBuf)))));
  1098. return(bResult);
  1099. }
  1100. /*
  1101. ** GetRemotePathFromCNRLink()
  1102. **
  1103. **
  1104. **
  1105. ** Arguments:
  1106. **
  1107. ** Returns:
  1108. **
  1109. ** Side Effects: none
  1110. */
  1111. PUBLIC_CODE void GetRemotePathFromCNRLink(PCCNRLINK pccnrl,
  1112. LPTSTR pszRemotePathBuf)
  1113. {
  1114. ASSERT(IS_VALID_STRUCT_PTR(pccnrl, CCNRLINK));
  1115. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszRemotePathBuf, STR, MAX_PATH_LEN));
  1116. /* It's ok that this is broken for non-UNC CNR names. */
  1117. /* (- 1) for trailing slash. */
  1118. #ifdef UNICODE
  1119. ASSERT(IS_ICNRL_ANSI(pccnrl) ? (lstrlenA(ICNRL_Remote_Name_PtrA((PCICNRLINK)pccnrl)) < MAX_PATH_LEN - 1) :
  1120. (lstrlenW(ICNRL_Remote_Name_PtrW((PCICNRLINK)pccnrl)) < MAX_PATH_LEN - 1));
  1121. #else
  1122. ASSERT(lstrlenA(ICNRL_Remote_Name_PtrA((PCICNRLINK)pccnrl)) < MAX_PATH_LEN - 1);
  1123. #endif
  1124. #ifdef UNICODE
  1125. {
  1126. WCHAR szWideNetName[MAX_PATH];
  1127. LPWSTR pszWideNetName;
  1128. if (IS_ICNRL_ANSI(pccnrl))
  1129. {
  1130. pszWideNetName = szWideNetName;
  1131. MultiByteToWideChar(CP_ACP, 0,
  1132. ICNRL_Remote_Name_PtrA((PCICNRLINK)pccnrl), -1,
  1133. szWideNetName, MAX_PATH);
  1134. }
  1135. else
  1136. {
  1137. pszWideNetName = ICNRL_Remote_Name_Ptr((PCICNRLINK)pccnrl);
  1138. }
  1139. lstrcpy(pszRemotePathBuf, pszWideNetName);
  1140. }
  1141. #else
  1142. lstrcpy(pszRemotePathBuf, ICNRL_Remote_Name_Ptr((PCICNRLINK)pccnrl));
  1143. #endif
  1144. CatPath(pszRemotePathBuf, TEXT("\\"));
  1145. return;
  1146. }
  1147. /*
  1148. ** ConnectToCNR()
  1149. **
  1150. **
  1151. **
  1152. ** Arguments:
  1153. **
  1154. ** Returns:
  1155. **
  1156. ** Side Effects: none
  1157. */
  1158. PUBLIC_CODE BOOL ConnectToCNR(PCCNRLINK pccnrl, DWORD dwInFlags,
  1159. HWND hwndOwner, LPTSTR pszRootPathBuf,
  1160. PDWORD pdwOutFlags)
  1161. {
  1162. BOOL bResult = FALSE;
  1163. BOOL bValidDevice;
  1164. BOOL bRedirect;
  1165. BOOL bTryLastDevice = FALSE;
  1166. DWORD dwcbRootPathBufLen;
  1167. LPTSTR pszNetName;
  1168. LPTSTR pszDevice;
  1169. #ifdef UNICODE
  1170. WCHAR szWideNetName[MAX_PATH];
  1171. WCHAR szWideDevice[MAX_PATH];
  1172. #endif
  1173. ASSERT(IS_VALID_STRUCT_PTR(pccnrl, CCNRLINK));
  1174. ASSERT(FLAGS_ARE_VALID(dwInFlags, ALL_CONNECT_IN_FLAGS));
  1175. ASSERT(IS_FLAG_CLEAR(dwInFlags, CONNECT_INTERACTIVE) ||
  1176. IS_VALID_HANDLE(hwndOwner, WND));
  1177. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszRootPathBuf, STR, MAX_PATH_LEN));
  1178. ASSERT(IS_VALID_WRITE_PTR(pdwOutFlags, DWORD));
  1179. *pdwOutFlags = 0;
  1180. #ifdef UNICODE
  1181. if (IS_ICNRL_ANSI(pccnrl))
  1182. {
  1183. pszNetName = szWideNetName;
  1184. MultiByteToWideChar(CP_ACP, 0,
  1185. ICNRL_Remote_Name_PtrA((PCICNRLINK)pccnrl), -1,
  1186. szWideNetName, MAX_PATH);
  1187. }
  1188. else
  1189. {
  1190. pszNetName = ICNRL_Remote_Name_Ptr((PCICNRLINK)pccnrl);
  1191. }
  1192. #else
  1193. pszNetName = ICNRL_Remote_Name_Ptr((PCICNRLINK)pccnrl);
  1194. #endif
  1195. /* Do we have an old redirected device to try? */
  1196. bValidDevice = IS_FLAG_SET(((PCICNRLINK)pccnrl)->dwFlags,
  1197. ICNRL_FL_VALID_DEVICE);
  1198. #ifdef UNICODE
  1199. if ( bValidDevice )
  1200. {
  1201. if (IS_ICNRL_ANSI(pccnrl))
  1202. {
  1203. pszDevice = szWideDevice;
  1204. MultiByteToWideChar(CP_ACP, 0,
  1205. ICNRL_Device_PtrA((PCICNRLINK)pccnrl), -1,
  1206. szWideDevice, MAX_PATH);
  1207. }
  1208. else
  1209. {
  1210. pszDevice = ICNRL_Device_Ptr((PCICNRLINK)pccnrl);
  1211. }
  1212. }
  1213. #else
  1214. pszDevice = ICNRL_Device_Ptr((PCICNRLINK)pccnrl);
  1215. #endif
  1216. bRedirect = (bValidDevice || IS_FLAG_SET(dwInFlags, CONNECT_REDIRECT));
  1217. if (bRedirect)
  1218. {
  1219. if (bValidDevice)
  1220. {
  1221. DWORD dwNetResult;
  1222. /* "X:" + null terminator */
  1223. TCHAR rgchDrive[2 + 1];
  1224. /* Yes. Is it already connected to the desired CNR? */
  1225. TRACE_OUT((TEXT("ConnectToCNR(): Calling WNetGetConnection() to check %s for CNR \"%s\"."),
  1226. pszDevice, pszNetName));
  1227. dwcbRootPathBufLen = MAX_PATH_LEN;
  1228. /* WNetGetConnection requires the device name to have no trailing
  1229. ** backslash.
  1230. */
  1231. MyLStrCpyN(rgchDrive, pszDevice, ARRAYSIZE(rgchDrive));
  1232. dwNetResult = WNetGetConnection(rgchDrive, pszRootPathBuf, &dwcbRootPathBufLen);
  1233. if (dwNetResult == WN_SUCCESS)
  1234. {
  1235. if (CompareNetNames(pszNetName, pszRootPathBuf)
  1236. == CR_EQUAL)
  1237. {
  1238. TRACE_OUT((TEXT("ConnectToCNR(): Found matching CNR \"%s\" on %s."),
  1239. pszRootPathBuf,
  1240. pszDevice));
  1241. ASSERT(lstrlenA(ICNRL_Device_PtrA((PCICNRLINK)pccnrl)) < MAX_PATH_LEN);
  1242. lstrcpy(pszRootPathBuf, pszDevice);
  1243. bResult = TRUE;
  1244. }
  1245. else
  1246. TRACE_OUT((TEXT("ConnectToCNR(): Found unmatched CNR \"%s\" on %s."),
  1247. pszRootPathBuf,
  1248. pszDevice));
  1249. }
  1250. else
  1251. {
  1252. TRACE_OUT((TEXT("ConnectToCNR(): WNetGetConnection() failed on %s."),
  1253. pszDevice));
  1254. /*
  1255. * Only attempt a connection to the last redirected device if that
  1256. * device is not already in use.
  1257. */
  1258. bTryLastDevice = (GetDriveType(pszDevice)
  1259. == DRIVE_NO_ROOT_DIR);
  1260. }
  1261. }
  1262. if (! bResult)
  1263. /* See if the desired CNR is connected to any local device. */
  1264. bResult = SearchForRedirectedConnection((PCICNRLINK)pccnrl,
  1265. pszRootPathBuf);
  1266. /*
  1267. * Assume that no reference count is maintained for redirected device
  1268. * connections, so we do not have to add a found redirected device
  1269. * connection again.
  1270. */
  1271. }
  1272. if (! bResult)
  1273. {
  1274. NETRESOURCE nr;
  1275. TCHAR rgchNPName[MAX_PATH_LEN];
  1276. /* RAIDRAID: (15691) We only support disk resource connections here. */
  1277. ZeroMemory(&nr, SIZEOF(nr));
  1278. nr.lpRemoteName = pszNetName;
  1279. nr.dwType = RESOURCETYPE_DISK;
  1280. if (GetNetProviderName((PCICNRLINK)pccnrl, rgchNPName))
  1281. nr.lpProvider = rgchNPName;
  1282. /* Shall we try the old device? */
  1283. if (bTryLastDevice)
  1284. {
  1285. /* Yes. */
  1286. ASSERT(bValidDevice);
  1287. nr.lpLocalName = pszDevice;
  1288. WARNING_OUT((TEXT("ConnectToCNR(): Calling WNetUseConnection() to attempt to connect %s to CNR \"%s\"."),
  1289. nr.lpLocalName,
  1290. nr.lpRemoteName));
  1291. }
  1292. else
  1293. {
  1294. /* No. Shall we attempt to force a redirected connection? */
  1295. if (bValidDevice)
  1296. {
  1297. /*
  1298. * Yes. N.b., the caller may already have set CONNECT_REDIRECT in
  1299. * dwInFlags here.
  1300. */
  1301. SET_FLAG(dwInFlags, CONNECT_REDIRECT);
  1302. WARNING_OUT((TEXT("ConnectToCNR(): Calling WNetUseConnection() to establish auto-picked redirected connection to CNR \"%s\"."),
  1303. nr.lpRemoteName));
  1304. }
  1305. else
  1306. /* No. */
  1307. WARNING_OUT((TEXT("ConnectToCNR(): Calling WNetUseConnection() to establish connection to CNR \"%s\"."),
  1308. TEXT("<nr.lpRemoteName>"))); // nr.lpRemoteName));
  1309. ASSERT(! nr.lpLocalName);
  1310. }
  1311. dwcbRootPathBufLen = MAX_PATH_LEN;
  1312. bResult = (WNetUseConnection(hwndOwner, &nr, NULL, NULL, dwInFlags,
  1313. pszRootPathBuf, &dwcbRootPathBufLen,
  1314. pdwOutFlags)
  1315. == NO_ERROR);
  1316. }
  1317. if (bResult)
  1318. CatPath(pszRootPathBuf, TEXT("\\"));
  1319. ASSERT(! bResult ||
  1320. (IS_VALID_STRING_PTR(pszRootPathBuf, STR) &&
  1321. FLAGS_ARE_VALID(*pdwOutFlags, ALL_CONNECT_OUT_FLAGS)));
  1322. return(bResult);
  1323. }
  1324. /*
  1325. ** DisconnectFromCNR()
  1326. **
  1327. **
  1328. **
  1329. ** Arguments:
  1330. **
  1331. ** Returns:
  1332. **
  1333. ** Side Effects: none
  1334. */
  1335. PUBLIC_CODE BOOL DisconnectFromCNR(PCCNRLINK pccnrl)
  1336. {
  1337. DWORD dwNetResult;
  1338. LPTSTR pszNetName;
  1339. #ifdef UNICODE
  1340. WCHAR szWideNetName[MAX_PATH];
  1341. #endif
  1342. ASSERT(IS_VALID_STRUCT_PTR(pccnrl, CCNRLINK));
  1343. #ifdef UNICODE
  1344. if (IS_ICNRL_ANSI(pccnrl))
  1345. {
  1346. pszNetName = szWideNetName;
  1347. MultiByteToWideChar(CP_ACP, 0,
  1348. ICNRL_Remote_Name_PtrA((PCICNRLINK)pccnrl), -1,
  1349. szWideNetName, MAX_PATH);
  1350. }
  1351. else
  1352. {
  1353. pszNetName = ICNRL_Remote_Name_Ptr((PCICNRLINK)pccnrl);
  1354. }
  1355. #else
  1356. pszNetName = ICNRL_Remote_Name_Ptr((PCICNRLINK)pccnrl);
  1357. #endif
  1358. dwNetResult = WNetCancelConnection2(pszNetName,
  1359. CONNECT_REFCOUNT, FALSE);
  1360. if (dwNetResult == NO_ERROR)
  1361. WARNING_OUT((TEXT("DisconnectFromCNR(): Reduced connection reference count on CNR \"%s\"."),
  1362. pszNetName));
  1363. else
  1364. WARNING_OUT((TEXT("DisconnectFromCNR(): Failed to reduce connection reference count on CNR \"%s\". WNetCancelConnection2() returned %lu."),
  1365. pszNetName));
  1366. return(dwNetResult == NO_ERROR);
  1367. }
  1368. /*
  1369. ** IsCNRAvailable()
  1370. **
  1371. **
  1372. **
  1373. ** Arguments:
  1374. **
  1375. ** Returns:
  1376. **
  1377. ** Side Effects: none
  1378. */
  1379. PUBLIC_CODE BOOL IsCNRAvailable(PCCNRLINK pccnrl)
  1380. {
  1381. TCHAR rgchCNRRoot[MAX_PATH_LEN];
  1382. LPTSTR pszNetName;
  1383. #ifdef UNICODE
  1384. WCHAR szWideNetName[MAX_PATH];
  1385. #endif
  1386. ASSERT(IS_VALID_STRUCT_PTR(pccnrl, CCNRLINK));
  1387. #ifdef UNICODE
  1388. if (IS_ICNRL_ANSI(pccnrl))
  1389. {
  1390. pszNetName = szWideNetName;
  1391. MultiByteToWideChar(CP_ACP, 0,
  1392. ICNRL_Remote_Name_PtrA((PCICNRLINK)pccnrl), -1,
  1393. szWideNetName, MAX_PATH);
  1394. }
  1395. else
  1396. {
  1397. pszNetName = ICNRL_Remote_Name_Ptr((PCICNRLINK)pccnrl);
  1398. }
  1399. #else
  1400. pszNetName = ICNRL_Remote_Name_Ptr((PCICNRLINK)pccnrl);
  1401. #endif
  1402. ASSERT(lstrlen(pszNetName) < ARRAYSIZE(rgchCNRRoot) - 1);
  1403. lstrcpy(rgchCNRRoot, pszNetName);
  1404. CatPath(rgchCNRRoot, TEXT("\\"));
  1405. return(PathExists(rgchCNRRoot));
  1406. }
  1407. /*
  1408. ** GetCNRLinkLen()
  1409. **
  1410. **
  1411. **
  1412. ** Arguments:
  1413. **
  1414. ** Returns:
  1415. **
  1416. ** Side Effects: none
  1417. */
  1418. PUBLIC_CODE UINT GetCNRLinkLen(PCCNRLINK pccnrl)
  1419. {
  1420. ASSERT(IS_VALID_STRUCT_PTR(pccnrl, CCNRLINK));
  1421. return(((PCICNRLINK)pccnrl)->ucbSize);
  1422. }
  1423. /*
  1424. ** GetCNRNetType()
  1425. **
  1426. **
  1427. **
  1428. ** Arguments:
  1429. **
  1430. ** Returns:
  1431. **
  1432. ** Side Effects: none
  1433. */
  1434. PUBLIC_CODE BOOL GetCNRNetType(PCCNRLINK pccnrl, PCDWORD *ppcdwNetType)
  1435. {
  1436. BOOL bResult;
  1437. ASSERT(IS_VALID_STRUCT_PTR(pccnrl, CCNRLINK));
  1438. bResult = IS_FLAG_SET(((PCICNRLINK)pccnrl)->dwFlags,
  1439. ICNRL_FL_VALID_NET_TYPE);
  1440. if (bResult)
  1441. *ppcdwNetType = &(((PCICNRLINK)pccnrl)->dwNetType);
  1442. ASSERT(! bResult ||
  1443. IsValidNetType(**ppcdwNetType));
  1444. return(bResult);
  1445. }
  1446. /*
  1447. ** GetCNRName()
  1448. **
  1449. **
  1450. **
  1451. ** Arguments:
  1452. **
  1453. ** Returns:
  1454. **
  1455. ** Side Effects: none
  1456. */
  1457. PUBLIC_CODE BOOL GetCNRName(PCCNRLINK pccnrl, LPCSTR *ppcszCNRName)
  1458. {
  1459. ASSERT(IS_VALID_STRUCT_PTR(pccnrl, CCNRLINK));
  1460. *ppcszCNRName = ICNRL_Remote_Name_PtrA((PCICNRLINK)pccnrl);
  1461. ASSERT(IS_VALID_STRING_PTRA(*ppcszCNRName, CSTR));
  1462. return(TRUE);
  1463. }
  1464. #ifdef UNICODE
  1465. /*
  1466. ** GetCNRNameW()
  1467. **
  1468. **
  1469. **
  1470. ** Arguments:
  1471. **
  1472. ** Returns:
  1473. **
  1474. ** Side Effects: none
  1475. */
  1476. PUBLIC_CODE BOOL GetCNRNameW(PCCNRLINK pccnrl, LPCWSTR *ppcszCNRName)
  1477. {
  1478. ASSERT(IS_VALID_STRUCT_PTR(pccnrl, CCNRLINK));
  1479. if (IS_ICNRL_ANSI(pccnrl))
  1480. *ppcszCNRName = NULL;
  1481. else
  1482. {
  1483. *ppcszCNRName = ICNRL_Remote_Name_Ptr((PCICNRLINK)pccnrl);
  1484. ASSERT(IS_VALID_STRING_PTR(*ppcszCNRName, CSTR));
  1485. }
  1486. return(TRUE);
  1487. }
  1488. #endif
  1489. /*
  1490. ** GetLastRedirectedDevice()
  1491. **
  1492. **
  1493. **
  1494. ** Arguments:
  1495. **
  1496. ** Returns:
  1497. **
  1498. ** Side Effects: none
  1499. */
  1500. PUBLIC_CODE BOOL GetLastRedirectedDevice(PCCNRLINK pccnrl, LPCSTR *ppcszDevice)
  1501. {
  1502. BOOL bResult;
  1503. ASSERT(IS_VALID_STRUCT_PTR(pccnrl, CCNRLINK));
  1504. bResult = IS_FLAG_SET(((PCICNRLINK)pccnrl)->dwFlags, ICNRL_FL_VALID_DEVICE);
  1505. if (bResult)
  1506. *ppcszDevice = ICNRL_Device_PtrA((PCICNRLINK)pccnrl);
  1507. ASSERT(! bResult ||
  1508. IS_VALID_STRING_PTRA(*ppcszDevice, CSTR));
  1509. return(bResult);
  1510. }
  1511. #ifdef UNICODE
  1512. /*
  1513. ** GetLastRedirectedDeviceW()
  1514. **
  1515. **
  1516. **
  1517. ** Arguments:
  1518. **
  1519. ** Returns:
  1520. **
  1521. ** Side Effects: none
  1522. */
  1523. PUBLIC_CODE BOOL GetLastRedirectedDeviceW(PCCNRLINK pccnrl, LPCWSTR *ppcszDevice)
  1524. {
  1525. BOOL bResult;
  1526. ASSERT(IS_VALID_STRUCT_PTR(pccnrl, CCNRLINK));
  1527. bResult = IS_FLAG_SET(((PCICNRLINK)pccnrl)->dwFlags, ICNRL_FL_VALID_DEVICE);
  1528. if (bResult)
  1529. if (IS_ICNRL_ANSI(pccnrl))
  1530. *ppcszDevice = NULL;
  1531. else
  1532. {
  1533. *ppcszDevice = ICNRL_Device_Ptr((PCICNRLINK)pccnrl);
  1534. ASSERT(! bResult ||
  1535. IS_VALID_STRING_PTR(*ppcszDevice, CSTR));
  1536. }
  1537. return(bResult);
  1538. }
  1539. #endif
  1540. #if defined(DEBUG) || defined (VSTF)
  1541. /*
  1542. ** IsValidPCCNRLINK()
  1543. **
  1544. **
  1545. **
  1546. ** Arguments:
  1547. **
  1548. ** Returns:
  1549. **
  1550. ** Side Effects: none
  1551. */
  1552. PUBLIC_CODE BOOL IsValidPCCNRLINK(PCCNRLINK pccnrl)
  1553. {
  1554. return(IS_VALID_STRUCT_PTR((PCICNRLINK)pccnrl, CICNRLINK));
  1555. }
  1556. #endif
  1557. #ifdef DEBUG
  1558. /*
  1559. ** DumpCNRLink()
  1560. **
  1561. **
  1562. **
  1563. ** Arguments:
  1564. **
  1565. ** Returns:
  1566. **
  1567. ** Side Effects: none
  1568. */
  1569. PUBLIC_CODE void DumpCNRLink(PCCNRLINK pccnrl)
  1570. {
  1571. ASSERT(IS_VALID_STRUCT_PTR(pccnrl, CCNRLINK));
  1572. PLAIN_TRACE_OUT((TEXT("%s%s[CNR link] ucbSize %#x"),
  1573. INDENT_STRING,
  1574. INDENT_STRING,
  1575. ((PCICNRLINK)pccnrl)->ucbSize));
  1576. PLAIN_TRACE_OUT((TEXT("%s%s[CNR link] dwFLags = %#08lx"),
  1577. INDENT_STRING,
  1578. INDENT_STRING,
  1579. ((PCICNRLINK)pccnrl)->dwFlags));
  1580. PLAIN_TRACE_OUT((TEXT("%s%s[CNR link] CNR name \"%s\""),
  1581. INDENT_STRING,
  1582. INDENT_STRING,
  1583. ICNRL_Remote_Name_Ptr((PCICNRLINK)pccnrl)));
  1584. if (IS_FLAG_SET(((PCICNRLINK)pccnrl)->dwFlags, ICNRL_FL_VALID_NET_TYPE))
  1585. PLAIN_TRACE_OUT((TEXT("%s%s[CNR link] net type %#08lx"),
  1586. INDENT_STRING,
  1587. INDENT_STRING,
  1588. ((PCICNRLINK)pccnrl)->dwNetType));
  1589. else
  1590. PLAIN_TRACE_OUT((TEXT("%s%s[CNR link] net type unknown"),
  1591. INDENT_STRING,
  1592. INDENT_STRING));
  1593. if (IS_FLAG_SET(((PCICNRLINK)pccnrl)->dwFlags, ICNRL_FL_VALID_DEVICE))
  1594. PLAIN_TRACE_OUT((TEXT("%s%s[CNR link] last redirected local device \"%s\""),
  1595. INDENT_STRING,
  1596. INDENT_STRING,
  1597. ICNRL_Device_Ptr((PCICNRLINK)pccnrl)));
  1598. else
  1599. PLAIN_TRACE_OUT((TEXT("%s%s[CNR link] no last redirected local device"),
  1600. INDENT_STRING,
  1601. INDENT_STRING));
  1602. return;
  1603. }
  1604. #endif