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.

2237 lines
61 KiB

  1. /********************************************************************/
  2. /** Microsoft LAN Manager **/
  3. /** Copyright(c) Microsoft Corp., 1987-1991 **/
  4. /********************************************************************/
  5. /***
  6. * use.c
  7. * Functions for displaying and manipulating network uses
  8. * Redirected device can only be: disks a: to z:;
  9. * comm devs com1[:] to com9[:]; and lpt1[:] to lpt9[:].
  10. * NOTE: even though it uses the WNet*** calls, it is not
  11. * intended to be used for networks other than LM.
  12. * we use WNet purely to leverage off the persistent
  13. * connections.
  14. *
  15. * History:
  16. * mm/dd/yy, who, comment
  17. * 06/09/87, andyh, new code
  18. * 07/02/87, andyh, del with ucond = 1
  19. * 10/31/88, erichn, uses OS2.H instead of DOSCALLS
  20. * 01/04/89, erichn, filenames now MAX_PATH LONG
  21. * 05/02/89, erichn, NLS conversion
  22. * 05/09/89, erichn, local security mods
  23. * 05/19/89, erichn, NETCMD output sorting
  24. * 06/08/89, erichn, canonicalization sweep
  25. * 06/23/89, erichn, replaced old NetI canon calls with new I_Net
  26. * 03/03/90, thomaspa, INTERNAL retry with mixed case for
  27. * password errors from down-level servers
  28. * 03/06/90, thomaspa, integrate INTERNAL to shipped product
  29. * 02/09/91, danhi, change to use lm 16/32 mapping layer
  30. * 02/20/91, robdu, added profile update code
  31. * 02/18/92, chuckc, use WNet*** to handle sticky connections (part I)
  32. * 04/25/92, jonn, removed two cases for build fix
  33. * 09/21/92 keithmo, use unicode versions of WNet*** API.
  34. */
  35. /* Include files */
  36. #define INCL_NOCOMMON
  37. #define INCL_DOSFILEMGR
  38. #define INCL_ERRORS
  39. #define INCL_ERROR_H
  40. #include <os2.h>
  41. #include <search.h>
  42. #include <lmcons.h>
  43. #include <lmerr.h>
  44. #include <lmapibuf.h>
  45. #include <lmaccess.h>
  46. #include <lmuse.h>
  47. #include <apperr.h>
  48. #include <apperr2.h>
  49. #include <icanon.h>
  50. #include <lui.h>
  51. #include <wincred.h>
  52. #include <dlwksta.h>
  53. #include "mwksta.h"
  54. #include "netcmds.h"
  55. #include "nettext.h"
  56. #include "msystem.h"
  57. // pull in the win32 headers
  58. #include <mpr.h> // for MPR_* manifests
  59. //
  60. // structure for combines LM and WNet info
  61. //
  62. typedef struct _NET_USE_INFO {
  63. LPWSTR lpLocalName ;
  64. LPWSTR lpRemoteName ;
  65. LPWSTR lpProviderName ;
  66. DWORD dwType ;
  67. DWORD dwStatus ;
  68. DWORD dwRefCount ;
  69. DWORD dwUseCount ;
  70. BOOL fIsLanman ;
  71. } NET_USE_INFO ;
  72. /* Static variables */
  73. TCHAR *LanmanProviderName = NULL ;
  74. /* Forward declarations */
  75. VOID LanmanDisplayUse(LPUSE_INFO_1);
  76. int NEAR is_admin_dollar(TCHAR FAR *);
  77. int __cdecl CmpUseInfo(const VOID FAR *, const VOID FAR *);
  78. VOID NEAR UseInit(VOID);
  79. USHORT QueryDefaultPersistence(BOOL *pfRemember) ;
  80. DWORD SetDefaultPersistence(BOOL fRemember) ;
  81. BOOL CheckIfWantUpdate(TCHAR *dev, TCHAR *resource) ;
  82. VOID WNetErrorExit(ULONG err);
  83. WCHAR *GetLanmanProviderName(void) ;
  84. TCHAR *MapWildCard(TCHAR *dev, TCHAR *startdev) ;
  85. DWORD MprUseEnum(PDWORD num_read,
  86. NET_USE_INFO **NetUseInfoBuffer,
  87. PDWORD NetUseInfoCount);
  88. DWORD LanmanUseAugment(DWORD num_read,
  89. NET_USE_INFO *NetUseInfoBuffer) ;
  90. DWORD UnavailUseAugment(PDWORD num_read,
  91. NET_USE_INFO **NetUseInfoBuffer,
  92. PDWORD NetUseInfoCount);
  93. VOID MprUseDisplay(TCHAR *dev) ;
  94. VOID use_del_all() ;
  95. /* Externs */
  96. extern int YorN_Switch;
  97. /* Message related definitions */
  98. #define USE_STATUS_OK 0
  99. #define USE_STATUS_PAUSED ( USE_STATUS_OK + 1 )
  100. #define USE_STATUS_SESSION_LOST ( USE_STATUS_PAUSED + 1 )
  101. #define USE_STATUS_NET_ERROR ( USE_STATUS_SESSION_LOST + 1 )
  102. #define USE_STATUS_CONNECTING ( USE_STATUS_NET_ERROR + 1 )
  103. #define USE_STATUS_RECONNECTING ( USE_STATUS_CONNECTING + 1 )
  104. #define USE_STATUS_UNAVAIL ( USE_STATUS_RECONNECTING + 1 )
  105. #ifdef DEBUG
  106. #define USE_STATUS_UNKNOWN ( USE_STATUS_UNAVAIL + 1 )
  107. #endif
  108. #define USE_REMEMBERED 0xFFFE
  109. static MESSAGE UseStatusList[] =
  110. {
  111. { APE2_USE_STATUS_OK, NULL },
  112. { APE2_USE_STATUS_PAUSED, NULL },
  113. { APE2_USE_STATUS_SESSION_LOST, NULL },
  114. { APE2_USE_STATUS_NET_ERROR, NULL },
  115. { APE2_USE_STATUS_CONNECTING, NULL },
  116. { APE2_USE_STATUS_RECONNECTING, NULL },
  117. { APE2_USE_STATUS_UNAVAIL, NULL }
  118. #ifdef DEBUG
  119. ,
  120. { APE2_GEN_UNKNOWN, NULL }
  121. #endif
  122. };
  123. #define NUM_STATUS_MSGS (sizeof(UseStatusList)/sizeof(UseStatusList[0]))
  124. #define USE_MSG_LOCAL 0
  125. #define USE_MSG_REMOTE ( USE_MSG_LOCAL + 1 )
  126. #define USE_MSG_TYPE ( USE_MSG_REMOTE + 1 )
  127. #define USE_TYPE_TBD ( USE_MSG_TYPE + 1 )
  128. #define USE_MSG_STATUS ( USE_TYPE_TBD + 1 )
  129. #define USE_STATUS_TBD ( USE_MSG_STATUS + 1 )
  130. #define USE_MSG_OPEN_COUNT ( USE_STATUS_TBD + 1 )
  131. #define USE_MSG_USE_COUNT ( USE_MSG_OPEN_COUNT + 1 )
  132. static MESSAGE UseMsgList[] =
  133. {
  134. { APE2_USE_MSG_LOCAL, NULL },
  135. { APE2_USE_MSG_REMOTE, NULL },
  136. { APE2_USE_MSG_TYPE, NULL },
  137. { APE2_GEN_UNKNOWN /* ie, TBD */, NULL },
  138. { APE2_USE_MSG_STATUS, NULL },
  139. { APE2_GEN_UNKNOWN /* ie, TBD */, NULL },
  140. { APE2_USE_MSG_OPEN_COUNT, NULL },
  141. { APE2_USE_MSG_USE_COUNT, NULL }
  142. };
  143. #define NUM_USE_MSGS (sizeof(UseMsgList)/sizeof(UseMsgList[0]))
  144. /***
  145. * use_display_all()
  146. * Display all network uses
  147. *
  148. * Args:
  149. * none
  150. *
  151. * Returns:
  152. * 0 - success
  153. * exit(2) - command failed
  154. */
  155. VOID
  156. use_display_all(
  157. VOID
  158. )
  159. {
  160. DWORD err; /* API return status */
  161. DWORD num_read; /* num entries read by API */
  162. DWORD maxLen; /* max message length */
  163. DWORD i;
  164. int msgno;
  165. BOOL fRemember ;
  166. DWORD NetUseInfoCount = 0 ;
  167. NET_USE_INFO *NetUseInfoBuffer = NULL ;
  168. NET_USE_INFO *pNetUseInfo ;
  169. UseInit();
  170. if (err = MprUseEnum(&num_read, &NetUseInfoBuffer, &NetUseInfoCount))
  171. ErrorExit(err);
  172. if (err = UnavailUseAugment(&num_read, &NetUseInfoBuffer, &NetUseInfoCount))
  173. ErrorExit(err);
  174. if (err = LanmanUseAugment(num_read, NetUseInfoBuffer))
  175. ErrorExit(err);
  176. if (QueryDefaultPersistence(&fRemember) == NERR_Success)
  177. InfoPrint(fRemember ? APE_ConnectionsAreRemembered :
  178. APE_ConnectionsAreNotRemembered);
  179. else
  180. InfoPrint(APE_ProfileReadError);
  181. if (num_read == 0)
  182. EmptyExit();
  183. for (i = 0, pNetUseInfo = NetUseInfoBuffer;
  184. i < num_read; i++, pNetUseInfo++)
  185. {
  186. //
  187. // if we find at least one entry we will display, break
  188. //
  189. if (!(pNetUseInfo->fIsLanman)
  190. || (pNetUseInfo->dwStatus != USE_OK)
  191. || (pNetUseInfo->dwUseCount != 0)
  192. || (pNetUseInfo->dwRefCount != 0))
  193. break;
  194. }
  195. if (i == num_read)
  196. EmptyExit(); // loop reached limit, so no entries to display
  197. qsort(NetUseInfoBuffer,
  198. num_read,
  199. sizeof(NET_USE_INFO), CmpUseInfo);
  200. GetMessageList(NUM_STATUS_MSGS, UseStatusList, &maxLen);
  201. PrintNL();
  202. InfoPrint(APE2_USE_HEADER);
  203. PrintLine();
  204. for (i = 0, pNetUseInfo = NetUseInfoBuffer;
  205. i < num_read;
  206. i++, pNetUseInfo++)
  207. {
  208. TCHAR *status_string ;
  209. switch(pNetUseInfo->dwStatus)
  210. {
  211. case USE_OK:
  212. if ((pNetUseInfo->dwUseCount == 0)
  213. && (pNetUseInfo->dwRefCount == 0)
  214. && pNetUseInfo->fIsLanman)
  215. continue;
  216. else
  217. msgno = USE_STATUS_OK;
  218. break;
  219. case USE_PAUSED:
  220. msgno = USE_STATUS_PAUSED;
  221. break;
  222. case USE_SESSLOST:
  223. msgno = USE_STATUS_SESSION_LOST;
  224. break;
  225. case USE_NETERR:
  226. msgno = USE_STATUS_NET_ERROR;
  227. break;
  228. case USE_CONN:
  229. msgno = USE_STATUS_CONNECTING;
  230. break;
  231. case USE_REMEMBERED:
  232. msgno = USE_STATUS_UNAVAIL;
  233. break;
  234. case USE_RECONN:
  235. msgno = USE_STATUS_RECONNECTING;
  236. break;
  237. default:
  238. msgno = -1;
  239. break;
  240. }
  241. if (msgno != -1)
  242. status_string = UseStatusList[msgno].msg_text ;
  243. else
  244. status_string = TEXT("") ;
  245. {
  246. TCHAR Buffer1[13],Buffer2[10],Buffer3[MAX_PATH + 1];
  247. if( wcslen( pNetUseInfo->lpRemoteName ) <= 25 ) {
  248. WriteToCon(TEXT("%Fs %Fs %Fs %Fs\r\n"),
  249. PaddedString(12,status_string,Buffer1),
  250. PaddedString( 9,pNetUseInfo->lpLocalName,Buffer2),
  251. PaddedString(25,pNetUseInfo->lpRemoteName,Buffer3),
  252. pNetUseInfo->lpProviderName);
  253. }
  254. else
  255. {
  256. TCHAR Buffer4[13],Buffer5[10],Buffer6[25];
  257. WriteToCon(TEXT("%Fs %Fs %Fs \r\n%Fs %Fs %Fs %Fs\r\n"),
  258. PaddedString(12,status_string,Buffer1),
  259. PaddedString( 9,pNetUseInfo->lpLocalName,Buffer2),
  260. PaddedString(wcslen(pNetUseInfo->lpRemoteName),
  261. pNetUseInfo->lpRemoteName,
  262. Buffer3),
  263. PaddedString(12,TEXT(""),Buffer4),
  264. PaddedString(9,TEXT(""),Buffer5),
  265. PaddedString(25,TEXT(""),Buffer6),
  266. pNetUseInfo->lpProviderName);
  267. }
  268. }
  269. }
  270. FreeMem((LPBYTE)NetUseInfoBuffer);
  271. InfoSuccess();
  272. }
  273. /***
  274. * LanmanUseAugment()
  275. * Enumerate uses from Lanman
  276. *
  277. * Args:
  278. * none
  279. *
  280. * Returns:
  281. * 0 - success
  282. * exit(2) - command failed
  283. */
  284. DWORD
  285. LanmanUseAugment(
  286. DWORD num_read,
  287. NET_USE_INFO *NetUseInfoBuffer
  288. )
  289. {
  290. DWORD dwErr;
  291. DWORD cTotalAvail;
  292. LPSTR pBuffer;
  293. DWORD numLMread; /* num entries read by API */
  294. DWORD j;
  295. DWORD i;
  296. LPUSE_INFO_1 use_entry;
  297. NET_USE_INFO *pNetUseInfo = NetUseInfoBuffer ;
  298. dwErr = NetUseEnum(NULL, 1, &pBuffer, MAX_PREFERRED_LENGTH,
  299. &numLMread, &cTotalAvail, NULL);
  300. if (dwErr != NERR_Success)
  301. {
  302. // consider as success (ie. there are no Lanman ones)
  303. return NERR_Success;
  304. }
  305. if (numLMread == 0)
  306. {
  307. return NERR_Success;
  308. }
  309. //
  310. // for all MPR returned entries that are Lanman uses,
  311. // augment with extra info if we have it.
  312. //
  313. for (i = 0; i < num_read; i++, pNetUseInfo++)
  314. {
  315. //
  316. // not LM, skip it
  317. //
  318. if (!(pNetUseInfo->fIsLanman))
  319. continue ;
  320. //
  321. // lets find it in the NetUseEnum return data
  322. //
  323. for (j = 0, use_entry = (LPUSE_INFO_1) pBuffer;
  324. j < numLMread; j++, use_entry++)
  325. {
  326. //
  327. // look for match. if device names are present & match, we've found
  328. // one. else we match only if remote names match *and* both device
  329. // names are not present.
  330. //
  331. TCHAR *local = use_entry->ui1_local ;
  332. TCHAR *remote = use_entry->ui1_remote ;
  333. if ( (local && *local && !_tcsicmp(pNetUseInfo->lpLocalName,local))
  334. ||
  335. ( (!local || !*local) &&
  336. !*(pNetUseInfo->lpLocalName) &&
  337. !_tcsicmp(pNetUseInfo->lpRemoteName,remote)
  338. )
  339. )
  340. {
  341. //
  342. // found the device in the LM list or
  343. // found as deviceless connection
  344. //
  345. pNetUseInfo->dwUseCount = use_entry->ui1_usecount ;
  346. pNetUseInfo->dwRefCount = use_entry->ui1_refcount ;
  347. pNetUseInfo->dwStatus = use_entry->ui1_status ;
  348. break ;
  349. }
  350. }
  351. }
  352. NetApiBufferFree(pBuffer);
  353. return NERR_Success;
  354. }
  355. /***
  356. * MprUseEnum()
  357. * Enumerates uses returned by WNET
  358. *
  359. * Args:
  360. * none
  361. *
  362. * Returns:
  363. * 0 - success
  364. * exit(2) - command failed
  365. */
  366. DWORD
  367. MprUseEnum(
  368. LPDWORD num_read,
  369. NET_USE_INFO **NetUseInfoBuffer,
  370. LPDWORD NetUseInfoCount
  371. )
  372. {
  373. DWORD EntriesRead = 0;
  374. LPBYTE Buffer;
  375. DWORD dwErr;
  376. DWORD dwAllocErr;
  377. HANDLE EnumHandle;
  378. DWORD BufferSize, Count;
  379. static TCHAR *NullString = TEXT("");
  380. //
  381. // initialize
  382. //
  383. *num_read = 0;
  384. *NetUseInfoCount = 64; // assume 64 entries initially. realloc if need
  385. if (dwAllocErr = AllocMem( *NetUseInfoCount * sizeof(NET_USE_INFO),
  386. (LPBYTE *) NetUseInfoBuffer ))
  387. {
  388. ErrorExit(dwAllocErr);
  389. }
  390. //
  391. // allocate memory and open the enumeration
  392. //
  393. if (dwAllocErr = AllocMem(BufferSize = 4096, &Buffer))
  394. {
  395. ErrorExit(dwAllocErr);
  396. }
  397. dwErr = WNetOpenEnum(RESOURCE_CONNECTED, 0, 0, NULL, &EnumHandle) ;
  398. if (dwErr != WN_SUCCESS)
  399. {
  400. return dwErr;
  401. }
  402. do
  403. {
  404. Count = 0xFFFFFFFF ;
  405. dwErr = WNetEnumResource(EnumHandle, &Count, Buffer, &BufferSize) ;
  406. if (dwErr == WN_SUCCESS || dwErr == WN_NO_MORE_ENTRIES)
  407. {
  408. LPNETRESOURCE lpNetResource ;
  409. NET_USE_INFO *lpNetUseInfo ;
  410. DWORD i ;
  411. //
  412. // grow the buffer if need. note there are no
  413. // pointers that point back to the buffer, so we
  414. // should be fine with the realloc.
  415. //
  416. if (EntriesRead + Count >= *NetUseInfoCount)
  417. {
  418. //
  419. // make sure it can hold all the new data, and add 64
  420. // to reduce the number of reallocs
  421. //
  422. *NetUseInfoCount = EntriesRead + Count + 64;
  423. dwAllocErr = ReallocMem(*NetUseInfoCount * sizeof(NET_USE_INFO),
  424. (LPBYTE *)NetUseInfoBuffer) ;
  425. if (dwAllocErr != NERR_Success)
  426. return dwAllocErr;
  427. }
  428. lpNetResource = (LPNETRESOURCE) Buffer ;
  429. lpNetUseInfo = *NetUseInfoBuffer + EntriesRead ;
  430. //
  431. // stick the entries into the NetUseInfoBuffer
  432. //
  433. for ( i = 0;
  434. i < Count;
  435. i++,EntriesRead++,lpNetUseInfo++,lpNetResource++ )
  436. {
  437. lpNetUseInfo->lpLocalName = lpNetResource->lpLocalName ?
  438. lpNetResource->lpLocalName : NullString ;
  439. lpNetUseInfo->lpRemoteName = lpNetResource->lpRemoteName ?
  440. lpNetResource->lpRemoteName : NullString ;
  441. lpNetUseInfo->lpProviderName = lpNetResource->lpProvider ?
  442. lpNetResource->lpProvider : NullString ;
  443. lpNetUseInfo->dwType = lpNetResource->dwType ;
  444. lpNetUseInfo->fIsLanman =
  445. (_tcscmp(lpNetResource->lpProvider,LanmanProviderName)==0) ;
  446. lpNetUseInfo->dwStatus = 0xFFFFFFFF ;
  447. lpNetUseInfo->dwRefCount =
  448. lpNetUseInfo->dwUseCount = 0 ;
  449. }
  450. //
  451. // allocate a new buffer for next set, since we still need
  452. // data in the old one, we dont free it. Netcmd lets the
  453. // system clean up when it exits.
  454. //
  455. if (dwErr == WN_SUCCESS)
  456. {
  457. if (dwAllocErr = AllocMem(BufferSize = 4096, &Buffer))
  458. {
  459. ErrorExit(dwAllocErr);
  460. }
  461. }
  462. }
  463. else
  464. {
  465. return dwErr;
  466. }
  467. }
  468. while (dwErr == WN_SUCCESS);
  469. dwErr = WNetCloseEnum(EnumHandle) ; // we dont report any errors here
  470. *num_read = EntriesRead ;
  471. return NERR_Success ;
  472. }
  473. /***
  474. * UnavailUseAugment()
  475. * Enumerate unavail uses & tags them on.
  476. *
  477. * Args:
  478. * none
  479. *
  480. * Returns:
  481. * 0 - success
  482. * exit(2) - command failed
  483. */
  484. DWORD
  485. UnavailUseAugment(
  486. LPDWORD NumRead,
  487. NET_USE_INFO **NetUseInfoBuffer,
  488. LPDWORD NetUseInfoCount
  489. )
  490. {
  491. LPBYTE Buffer ;
  492. DWORD dwErr ;
  493. HANDLE EnumHandle ;
  494. DWORD BufferSize, Count, InitialUseInfoCount ;
  495. DWORD err ;
  496. static TCHAR *NullString = TEXT("") ;
  497. InitialUseInfoCount = *NumRead ;
  498. //
  499. // allocate memory and open the enumeration
  500. //
  501. if (err = AllocMem(BufferSize = 4096, &Buffer))
  502. {
  503. ErrorExit(err);
  504. }
  505. dwErr = WNetOpenEnum(RESOURCE_REMEMBERED, 0, 0, NULL, &EnumHandle) ;
  506. if (dwErr != WN_SUCCESS)
  507. {
  508. return dwErr;
  509. }
  510. do
  511. {
  512. Count = 0xFFFFFFFF ;
  513. dwErr = WNetEnumResource(EnumHandle, &Count, Buffer, &BufferSize) ;
  514. if (dwErr == WN_SUCCESS || dwErr == WN_NO_MORE_ENTRIES)
  515. {
  516. LPNETRESOURCE lpNetResource ;
  517. NET_USE_INFO *lpNetUseInfo ;
  518. DWORD i,j ;
  519. if (Count == 0xFFFFFFFF || Count == 0)
  520. break ;
  521. lpNetResource = (LPNETRESOURCE) Buffer ;
  522. //
  523. // for each entry, see if it is an unavail one
  524. //
  525. for ( i = 0;
  526. i < Count;
  527. i++,lpNetResource++ )
  528. {
  529. lpNetUseInfo = *NetUseInfoBuffer ;
  530. //
  531. // search thru the ones we already have
  532. //
  533. for (j = 0;
  534. j < InitialUseInfoCount;
  535. j++, ++lpNetUseInfo)
  536. {
  537. if (lpNetUseInfo->lpLocalName &&
  538. lpNetResource->lpLocalName)
  539. {
  540. if ( *lpNetUseInfo->lpLocalName != 0 )
  541. {
  542. // Use _tcsnicmp because the Net api returns an LPTX
  543. // redirection without the ':' whereas the WNet api
  544. // includes the ':'.
  545. if (_tcsnicmp(lpNetResource->lpLocalName,
  546. lpNetUseInfo->lpLocalName,
  547. _tcslen(lpNetUseInfo->lpLocalName))==0)
  548. {
  549. break;
  550. }
  551. }
  552. else if (*lpNetResource->lpLocalName == 0)
  553. {
  554. break ;
  555. }
  556. }
  557. }
  558. //
  559. // if we broke out early, this is already connected, so
  560. // we dont bother add an 'unavailable' entry.
  561. //
  562. if (j < InitialUseInfoCount)
  563. continue ;
  564. //
  565. // grow the buffer if need. note there are no
  566. // pointers that point back to the buffer, so we
  567. // should be fine with the realloc.
  568. //
  569. if (*NumRead >= *NetUseInfoCount)
  570. {
  571. //
  572. // make sure it can hold all the new data, and add 64
  573. // to reduce the number of reallocs
  574. //
  575. *NetUseInfoCount += 64 ;
  576. err = ReallocMem(*NetUseInfoCount * sizeof(NET_USE_INFO),
  577. (LPBYTE *) NetUseInfoBuffer);
  578. if (err != NERR_Success)
  579. {
  580. return err;
  581. }
  582. }
  583. lpNetUseInfo = *NetUseInfoBuffer + *NumRead ;
  584. lpNetUseInfo->lpLocalName = lpNetResource->lpLocalName ?
  585. lpNetResource->lpLocalName : NullString ;
  586. lpNetUseInfo->lpRemoteName = lpNetResource->lpRemoteName ?
  587. lpNetResource->lpRemoteName : NullString ;
  588. lpNetUseInfo->lpProviderName = lpNetResource->lpProvider ?
  589. lpNetResource->lpProvider : NullString ;
  590. lpNetUseInfo->dwType = lpNetResource->dwType ;
  591. lpNetUseInfo->fIsLanman = FALSE ; // no more info of interest
  592. lpNetUseInfo->dwStatus = USE_REMEMBERED ;
  593. lpNetUseInfo->dwRefCount =
  594. lpNetUseInfo->dwUseCount = 0 ;
  595. _tcsupr(lpNetUseInfo->lpLocalName) ;
  596. *NumRead += 1 ;
  597. }
  598. //
  599. // allocate a new buffer for next set, since we still need
  600. // data in the old one, we dont free it. Netcmd lets the
  601. // system clean up when it exits.
  602. //
  603. if (dwErr == WN_SUCCESS)
  604. {
  605. if (err = AllocMem(BufferSize = 4096, &Buffer))
  606. {
  607. ErrorExit(err);
  608. }
  609. }
  610. }
  611. else
  612. {
  613. return dwErr;
  614. }
  615. }
  616. while (dwErr == WN_SUCCESS) ;
  617. dwErr = WNetCloseEnum(EnumHandle) ; // we dont report any errors here
  618. return NERR_Success ;
  619. }
  620. /***
  621. * CmpUseInfo(use1,use2)
  622. *
  623. * Compares two USE_INFO_1 structures and returns a relative
  624. * lexical value, suitable for using in qsort.
  625. *
  626. */
  627. int __cdecl CmpUseInfo(const VOID FAR * use1, const VOID FAR * use2)
  628. {
  629. register USHORT localDev1, localDev2;
  630. register DWORD devType1, devType2;
  631. /* first sort by whether use has local device name */
  632. localDev1 = ((NET_USE_INFO *) use1)->lpLocalName[0];
  633. localDev2 = ((NET_USE_INFO *) use2)->lpLocalName[0];
  634. if (localDev1 && !localDev2)
  635. return -1;
  636. if (localDev2 && !localDev1)
  637. return +1;
  638. /* then sort by device type */
  639. devType1 = ((NET_USE_INFO *) use1)->dwType;
  640. devType2 = ((NET_USE_INFO *) use2)->dwType;
  641. if (devType1 != devType2)
  642. return( (devType1 < devType2) ? -1 : 1 );
  643. /* if local device, sort by local name */
  644. if (localDev1)
  645. return _tcsicmp ( ((NET_USE_INFO *) use1)->lpLocalName,
  646. ((NET_USE_INFO *) use2)->lpLocalName);
  647. else
  648. /* sort by remote name */
  649. return _tcsicmp ( ((NET_USE_INFO *) use1)->lpRemoteName,
  650. ((NET_USE_INFO *) use2)->lpRemoteName);
  651. }
  652. /***
  653. * use_unc()
  654. * Process "NET USE unc-name" command line (display or add)
  655. *
  656. * Args:
  657. * name - the unc name
  658. *
  659. * Returns:
  660. * 0 - success
  661. * exit(2) - command failed
  662. */
  663. VOID use_unc(TCHAR * name)
  664. {
  665. DWORD dwErr;
  666. LPUSE_INFO_1 use_entry;
  667. UseInit();
  668. if (dwErr = NetUseGetInfo(NULL,
  669. name,
  670. 1,
  671. (LPBYTE *) &use_entry))
  672. {
  673. //
  674. // hit an error, so just add it
  675. //
  676. NetApiBufferFree((LPBYTE) use_entry);
  677. use_add(NULL, name, NULL, FALSE, TRUE);
  678. }
  679. else
  680. {
  681. //
  682. // it is Lanman. treat it as we have in the past
  683. //
  684. if ((use_entry->ui1_usecount == 0) && (use_entry->ui1_refcount == 0))
  685. use_add(NULL, name, NULL, FALSE, FALSE);
  686. else
  687. LanmanDisplayUse(use_entry);
  688. NetApiBufferFree((CHAR FAR *) use_entry);
  689. InfoSuccess();
  690. }
  691. }
  692. /***
  693. * use_display_dev()
  694. * Display status of redirected device.
  695. *
  696. * Args:
  697. * dev - redirected device
  698. *
  699. * Returns:
  700. * 0 - success
  701. * exit(2) - command failed
  702. */
  703. VOID use_display_dev(TCHAR * dev)
  704. {
  705. DWORD dwErr;
  706. LPUSE_INFO_1 use_entry = NULL;
  707. if (IsWildCard(dev))
  708. help_help(0, USAGE_ONLY) ;
  709. UseInit();
  710. if (dwErr = NetUseGetInfo(NULL,
  711. dev,
  712. 1,
  713. (LPBYTE *) &use_entry))
  714. {
  715. //
  716. // Lanman failed, so try MPR
  717. //
  718. NetApiBufferFree((LPBYTE) use_entry);
  719. MprUseDisplay(dev) ;
  720. InfoSuccess();
  721. }
  722. else
  723. {
  724. LanmanDisplayUse(use_entry);
  725. NetApiBufferFree((CHAR FAR *) use_entry);
  726. InfoSuccess();
  727. }
  728. }
  729. VOID
  730. MprUseDisplay(
  731. TCHAR *dev
  732. )
  733. {
  734. DWORD dwErr;
  735. DWORD dwLength = 0;
  736. LPTSTR lpRemoteName;
  737. DWORD maxLen;
  738. //
  739. // Figure out how large a buffer we need
  740. //
  741. dwErr = WNetGetConnection(dev, NULL, &dwLength);
  742. if (dwErr != WN_MORE_DATA)
  743. {
  744. ErrorExit(dwErr);
  745. }
  746. dwErr = AllocMem(dwLength * sizeof(TCHAR), &lpRemoteName);
  747. if (dwErr != NERR_Success)
  748. {
  749. ErrorExit(dwErr);
  750. }
  751. dwErr = WNetGetConnection(dev, lpRemoteName, &dwLength);
  752. if (dwErr != WN_SUCCESS && dwErr != WN_CONNECTION_CLOSED)
  753. {
  754. ErrorExit(dwErr);
  755. }
  756. dwLength = _tcslen(dev);
  757. if (dwLength == 2 && _istalpha(dev[0]) && dev[1] == TEXT(':'))
  758. {
  759. UseMsgList[USE_TYPE_TBD].msg_number = APE2_USE_TYPE_DISK;
  760. }
  761. else if (dwLength >= 3 && _tcsnicmp(dev, TEXT("LPT"), 3) == 0)
  762. {
  763. UseMsgList[USE_TYPE_TBD].msg_number = APE2_USE_TYPE_PRINT;
  764. }
  765. else
  766. {
  767. UseMsgList[USE_TYPE_TBD].msg_number = APE2_GEN_UNKNOWN;
  768. }
  769. GetMessageList(NUM_USE_MSGS, UseMsgList, &maxLen);
  770. maxLen += 5;
  771. WriteToCon(fmtPSZ,0,maxLen,
  772. PaddedString(maxLen,UseMsgList[USE_MSG_LOCAL].msg_text,NULL),
  773. dev);
  774. WriteToCon(fmtPSZ,0,maxLen,
  775. PaddedString(maxLen,UseMsgList[USE_MSG_REMOTE].msg_text,NULL),
  776. lpRemoteName);
  777. WriteToCon(fmtNPSZ,0,maxLen,
  778. PaddedString(maxLen,UseMsgList[USE_MSG_TYPE].msg_text,NULL),
  779. UseMsgList[USE_TYPE_TBD].msg_text);
  780. FreeMem(lpRemoteName);
  781. return;
  782. }
  783. /***
  784. * use_add()
  785. * Add a redirection
  786. *
  787. * Args:
  788. * dev - local device to redirect
  789. * name - remote name to redirect to
  790. * pass - password to use when validating the use
  791. * comm - TRUE --> use as a char dev
  792. * print_ok - should a message be printed on success?
  793. *
  794. * Returns:
  795. * 0 - success
  796. * exit(2) - command failed
  797. */
  798. VOID use_add(TCHAR * dev, TCHAR * name, TCHAR * pass, int comm, int print_ok)
  799. {
  800. static TCHAR pbuf[(PWLEN>CREDUI_MAX_PASSWORD_LENGTH?PWLEN:CREDUI_MAX_PASSWORD_LENGTH)+1];
  801. static TCHAR UserBuffer[CREDUI_MAX_USERNAME_LENGTH+1];
  802. static TCHAR ServerNameBuffer[MAX_PATH+1];
  803. USHORT err; /* short return status */
  804. ULONG ulErr ; /* long return status */
  805. ULONG longtype; /* type field for I_NetPath */
  806. NETRESOURCEW netresource ;
  807. BOOL fRememberSwitch = FALSE ;
  808. BOOL fRemember = FALSE ;
  809. BOOL fSmartCard = FALSE;
  810. ULONG bConnectFlags = 0L ;
  811. LPWSTR pw_username = NULL;
  812. LPWSTR pw_pass = NULL;
  813. TCHAR *devicename = dev ;
  814. BOOL fExitCodeIsDrive = FALSE ;
  815. BOOL fSaveCred = FALSE;
  816. // unreferenced
  817. (void) comm ;
  818. UseInit();
  819. //
  820. // Build a non-UNC version of name to connect to.
  821. //
  822. if ( name != NULL ) {
  823. TCHAR *SlashPointer;
  824. //
  825. // Lob off the leading backslashes
  826. //
  827. if ( name[0] == '\\' && name[1] == '\\' ) {
  828. _tcscpy( ServerNameBuffer, &name[2] );
  829. } else {
  830. _tcscpy( ServerNameBuffer, name );
  831. }
  832. //
  833. // Lob off the share name
  834. //
  835. SlashPointer = _tcschr( ServerNameBuffer, '\\');
  836. if ( SlashPointer != NULL ) {
  837. *SlashPointer = '\0';
  838. }
  839. } else {
  840. ServerNameBuffer[0] = '\0';
  841. }
  842. // make sure we clean this up
  843. AddToMemClearList(pbuf, sizeof(pbuf), FALSE) ;
  844. // deal with any wild card Device specification
  845. if (devicename)
  846. {
  847. // If the devicname is a '?', then the exit code should be the ASCII
  848. // value of the drive that we connect.
  849. if (IsQuestionMark(devicename))
  850. {
  851. fExitCodeIsDrive = TRUE;
  852. }
  853. devicename = MapWildCard(devicename, NULL) ;
  854. if (!devicename)
  855. {
  856. // this can omly happen if no drives left
  857. ErrorExit(APE_UseWildCardNoneLeft) ;
  858. }
  859. }
  860. /* Initialize netresource structure */
  861. netresource.lpProvider = NULL ;
  862. netresource.lpLocalName = NULL ;
  863. netresource.lpRemoteName = NULL ;
  864. netresource.dwType = 0L ;
  865. if (devicename)
  866. {
  867. if (I_NetPathType(NULL, devicename, &longtype, 0L))
  868. ErrorExit(APE_UnknDevType);
  869. /*
  870. * NOTE: I would haved LOVED to have used a switch statement here.
  871. * But since types are now LONGS, and the compiler doesn't support
  872. * long switch statements, we're stuck with multiple if's. Sorry.
  873. */
  874. if (longtype == ITYPE_DEVICE_DISK)
  875. netresource.dwType = RESOURCETYPE_DISK;
  876. else if (longtype == ITYPE_DEVICE_LPT)
  877. netresource.dwType = RESOURCETYPE_PRINT;
  878. else if (longtype == ITYPE_DEVICE_COM)
  879. netresource.dwType = RESOURCETYPE_PRINT;
  880. else
  881. ErrorExit(APE_UnknDevType);
  882. netresource.lpLocalName = devicename ;
  883. }
  884. else
  885. {
  886. netresource.dwType = RESOURCETYPE_ANY;
  887. netresource.lpLocalName = L"" ;
  888. }
  889. if( name != NULL )
  890. {
  891. netresource.lpRemoteName = name ;
  892. }
  893. {
  894. USHORT i;
  895. // Find out if the /USER or /PERSISTENT switches are used
  896. for (i = 0; SwitchList[i]; i++)
  897. {
  898. //
  899. // Handle the /PERSISTENT switch
  900. //
  901. if ( !(_tcsncmp(SwitchList[i],
  902. swtxt_SW_USE_PERSISTENT,
  903. _tcslen(swtxt_SW_USE_PERSISTENT))) )
  904. {
  905. LPTSTR ptr;
  906. DWORD res;
  907. DWORD answer;
  908. fRememberSwitch = TRUE;
  909. // find the colon separator
  910. if ((ptr = FindColon(SwitchList[i])) == NULL)
  911. {
  912. ErrorExit(APE_InvalidSwitchArg);
  913. }
  914. // parse string after colon for YES or NO
  915. if ((res = LUI_ParseYesNo(ptr,&answer)) != 0)
  916. {
  917. ErrorExitInsTxt(APE_CmdArgIllegal,SwitchList[i]);
  918. }
  919. fRemember = (answer == LUI_YES_VAL) ;
  920. //
  921. // Handle the /USER switch
  922. //
  923. }
  924. else if ( !(_tcsncmp(SwitchList[i],
  925. swtxt_SW_USE_USER,
  926. _tcslen(swtxt_SW_USE_USER))) )
  927. {
  928. PTCHAR ptr;
  929. // find the colon separator
  930. if ((ptr = FindColon(SwitchList[i])) == NULL)
  931. ErrorExit(APE_InvalidSwitchArg);
  932. pw_username = ptr;
  933. //
  934. // Handle the /SMARTCARD switch
  935. //
  936. } else if ( !(_tcscmp(SwitchList[i], swtxt_SW_USE_SMARTCARD ))) {
  937. fSmartCard = TRUE;
  938. //
  939. // Handle the /SAVECRED switch
  940. //
  941. } else if ( !(_tcscmp(SwitchList[i], swtxt_SW_USE_SAVECRED ))) {
  942. fSaveCred = TRUE;
  943. //
  944. // Handle the /Delete switch
  945. // (The parser really doesn't let this through.)
  946. //
  947. }
  948. else if ( !(_tcscmp(SwitchList[i], swtxt_SW_DELETE)) )
  949. {
  950. // what the heck? adding and deleting?
  951. ErrorExit(APE_ConflictingSwitches) ;
  952. }
  953. // ignore other switches
  954. }
  955. }
  956. // remember switch was not specified
  957. if (!fRememberSwitch)
  958. {
  959. if (QueryDefaultPersistence(&fRemember)!=NERR_Success)
  960. InfoPrint(APE_ProfileReadError);
  961. }
  962. //
  963. // /user and /savecred are mutually exclusive.
  964. // This is because the auth packages don't call cred man if the user name
  965. // is specified. Therefore, the auth package didn't pass the target info to cred man.
  966. // If there is no target info, the UI will save a server specific cred.
  967. //
  968. if ( pw_username != NULL && fSaveCred ) {
  969. ErrorExit(APE_ConflictingSwitches) ;
  970. }
  971. //
  972. // Handle /SMARTCARD switch.
  973. // Prompt for smart card credentials.
  974. //
  975. if ( fSmartCard ) {
  976. //
  977. // We don't know how to save smartcard creds
  978. //
  979. if ( fSaveCred ) {
  980. ErrorExit(APE_ConflictingSwitches) ;
  981. }
  982. //
  983. // If a user name was specified,
  984. // use it to select which smart card to use.
  985. //
  986. if ( pw_username != NULL ) {
  987. _tcscpy( UserBuffer, pw_username );
  988. } else {
  989. UserBuffer[0] = '\0';
  990. }
  991. //
  992. // If password is specified,
  993. // use it as the default PIN
  994. if ( pass != NULL ) {
  995. // Consider "*" to be the same as "not specified"
  996. if (! _tcscmp(pass, TEXT("*"))) {
  997. pbuf[0] = '\0';
  998. } else {
  999. if (err = LUI_CanonPassword(pass)) {
  1000. ErrorExit(err);
  1001. }
  1002. _tcscpy( pbuf, pass );
  1003. }
  1004. } else {
  1005. pbuf[0] = '\0';
  1006. }
  1007. //
  1008. // Call the common UI.
  1009. //
  1010. // RtlZeroMemory( &UiInfo, sizeof(UiInfo) );
  1011. // UiInfo.dwVersion = 1;
  1012. ulErr = CredUICmdLinePromptForCredentialsW(
  1013. ServerNameBuffer, // Target name
  1014. NULL, // No context
  1015. NO_ERROR, // No authentication error
  1016. UserBuffer,
  1017. CREDUI_MAX_USERNAME_LENGTH,
  1018. pbuf,
  1019. CREDUI_MAX_PASSWORD_LENGTH,
  1020. NULL, // SaveFlag not allowed unless flag is specified
  1021. CREDUI_FLAGS_REQUIRE_SMARTCARD |
  1022. CREDUI_FLAGS_DO_NOT_PERSIST );
  1023. if ( ulErr != NO_ERROR ) {
  1024. ErrorExit(ulErr);
  1025. }
  1026. pw_username = UserBuffer;
  1027. pw_pass = pbuf;
  1028. //
  1029. // Handle cases where the password is specified on the command line.
  1030. //
  1031. } else if (pass) {
  1032. //
  1033. // We don't know how to save creds we don't prompt for
  1034. //
  1035. if ( fSaveCred ) {
  1036. ErrorExit(APE_ConflictingSwitches) ;
  1037. }
  1038. if (! _tcscmp(pass, TEXT("*")))
  1039. {
  1040. pass = pbuf;
  1041. IStrings[0] = name;
  1042. ReadPass(pass, PWLEN, 0, APE_UsePassPrompt, 1, FALSE);
  1043. if (err = LUI_CanonPassword(pass))
  1044. ErrorExit(err);
  1045. }
  1046. else
  1047. {
  1048. if (err = LUI_CanonPassword(pass))
  1049. ErrorExit(err);
  1050. }
  1051. pw_pass = pass;
  1052. }
  1053. //
  1054. // Loop handling assigning to the next drive letter
  1055. //
  1056. do {
  1057. //if persistent, the check for clash with existing remembered connection
  1058. bConnectFlags = 0 ;
  1059. if (fRemember)
  1060. {
  1061. if (CheckIfWantUpdate(devicename, name))
  1062. bConnectFlags |= CONNECT_UPDATE_PROFILE ;
  1063. }
  1064. //
  1065. // Allow it to prompt for us if the credential aren't on the command line
  1066. //
  1067. if ( !pass && !fSmartCard) {
  1068. bConnectFlags |= CONNECT_INTERACTIVE | CONNECT_COMMANDLINE;
  1069. //
  1070. // If the caller wants to save both username and password,
  1071. // create an enterprise peristed cred.
  1072. //
  1073. if ( fSaveCred ) {
  1074. bConnectFlags |= CONNECT_CMD_SAVECRED;
  1075. }
  1076. }
  1077. ulErr = WNetAddConnection2(&netresource,
  1078. pw_pass,
  1079. pw_username,
  1080. bConnectFlags) ;
  1081. switch(ulErr)
  1082. {
  1083. case WN_SUCCESS:
  1084. if (fRememberSwitch)
  1085. {
  1086. DWORD dwErr = SetDefaultPersistence(fRemember);
  1087. if (dwErr != NERR_Success)
  1088. {
  1089. ErrorExit(dwErr);
  1090. }
  1091. }
  1092. if (print_ok)
  1093. {
  1094. if (IsWildCard(dev)) // if originally a wildcard
  1095. {
  1096. IStrings[0] = devicename;
  1097. IStrings[1] = name;
  1098. InfoPrintIns(APE_UseWildCardSuccess, 2) ;
  1099. }
  1100. InfoSuccess();
  1101. }
  1102. if (fExitCodeIsDrive)
  1103. {
  1104. MyExit((int)devicename[0]);
  1105. }
  1106. return;
  1107. case WN_BAD_PASSWORD:
  1108. case WN_ACCESS_DENIED:
  1109. case ERROR_LOGON_FAILURE:
  1110. WNetErrorExit(ulErr);
  1111. case ERROR_ALREADY_ASSIGNED:
  1112. if (!IsWildCard(dev))
  1113. ErrorExit(ERROR_ALREADY_ASSIGNED) ;
  1114. // Get another drive letter
  1115. (devicename[0])++;
  1116. devicename = MapWildCard(TEXT("*"), devicename) ;
  1117. if (!devicename)
  1118. {
  1119. // this can only happen if no drives left
  1120. ErrorExit(APE_UseWildCardNoneLeft) ;
  1121. }
  1122. netresource.lpLocalName = devicename ;
  1123. break;
  1124. case WN_BAD_NETNAME:
  1125. if (is_admin_dollar(name))
  1126. ErrorExit(APE_BadAdminConfig);
  1127. // else drop thru
  1128. default:
  1129. WNetErrorExit(ulErr);
  1130. }
  1131. } while ( ulErr == ERROR_ALREADY_ASSIGNED );
  1132. }
  1133. VOID
  1134. use_set_remembered(
  1135. VOID
  1136. )
  1137. {
  1138. PTCHAR ptr;
  1139. BOOL fRemember ;
  1140. // Find the /persistent switch
  1141. if ((ptr = FindColon(SwitchList[0])) == NULL)
  1142. ErrorExit(APE_InvalidSwitchArg);
  1143. if ( !(_tcscmp(SwitchList[0], swtxt_SW_USE_PERSISTENT) ) )
  1144. {
  1145. DWORD res;
  1146. DWORD answer;
  1147. if ((res = LUI_ParseYesNo(ptr,&answer)) != 0)
  1148. {
  1149. ErrorExitInsTxt(APE_CmdArgIllegal,SwitchList[0]) ;
  1150. }
  1151. fRemember = (answer == LUI_YES_VAL) ;
  1152. res = SetDefaultPersistence(fRemember);
  1153. if (res != NERR_Success)
  1154. {
  1155. ErrorExit(res);
  1156. }
  1157. }
  1158. else
  1159. {
  1160. ErrorExit(APE_InvalidSwitch);
  1161. }
  1162. InfoSuccess();
  1163. }
  1164. /***
  1165. * use_del()
  1166. * Delete a redirection
  1167. *
  1168. * Args:
  1169. * dev - device OR unc-name to delete
  1170. * print_ok - print success message?
  1171. *
  1172. * Returns:
  1173. * 0 - success
  1174. * exit(2) - command failed
  1175. */
  1176. VOID use_del(TCHAR * dev, BOOL deviceless, int print_ok)
  1177. {
  1178. ULONG ulErr; /* API return status */
  1179. BOOL fRememberSwitch = FALSE ;
  1180. ULONG bConnectFlags = 0L ;
  1181. USHORT i;
  1182. UseInit();
  1183. // Find out if the /PERSISTENT switch is used
  1184. for (i = 0; SwitchList[i]; i++)
  1185. {
  1186. if ( !(_tcscmp(SwitchList[i], swtxt_SW_USE_PERSISTENT)) )
  1187. ErrorExit(APE_InvalidSwitch);
  1188. }
  1189. if (IsWildCard(dev))
  1190. {
  1191. use_del_all() ;
  1192. goto gone;
  1193. }
  1194. bConnectFlags = CONNECT_UPDATE_PROFILE ; // deletes always update
  1195. if ((ulErr = WNetCancelConnection2( dev,
  1196. bConnectFlags,
  1197. FALSE)) != WN_SUCCESS)
  1198. {
  1199. if (ulErr != WN_OPEN_FILES)
  1200. WNetErrorExit(ulErr);
  1201. }
  1202. else
  1203. goto gone;
  1204. InfoPrintInsTxt(APE_OpenHandles, dev);
  1205. if (!YorN(APE_UseBlowAway, 0))
  1206. NetcmdExit(2);
  1207. if ((ulErr = WNetCancelConnection2( (LPWSTR)dev,
  1208. bConnectFlags,
  1209. TRUE )) != WN_SUCCESS)
  1210. WNetErrorExit(ulErr);
  1211. gone:
  1212. if (print_ok)
  1213. if ( IsWildCard( dev ) )
  1214. InfoSuccess();
  1215. else
  1216. InfoPrintInsTxt(APE_DelSuccess, dev);
  1217. }
  1218. /***
  1219. * use_del_all()
  1220. * Delete all redirections
  1221. *
  1222. * Args:
  1223. * none
  1224. *
  1225. * Returns:
  1226. * 0 - success
  1227. * exit(2) - command failed
  1228. */
  1229. VOID use_del_all()
  1230. {
  1231. DWORD err = 0; /* API return status */
  1232. ULONG ulErr = 0; /* WNet error */
  1233. ULONG ulFirstErr = 0; /* WNet error */
  1234. DWORD num_read = 0; /* num entries read by API */
  1235. DWORD i = 0,j = 0;
  1236. DWORD NetUseInfoCount = 0 ;
  1237. NET_USE_INFO *NetUseInfoBuffer = NULL ;
  1238. NET_USE_INFO *pNetUseInfo = NULL;
  1239. ULONG bConnectFlags = 0L ;
  1240. UseInit();
  1241. if (err = MprUseEnum(&num_read, &NetUseInfoBuffer, &NetUseInfoCount))
  1242. ErrorExit(err);
  1243. if (err = UnavailUseAugment(&num_read, &NetUseInfoBuffer, &NetUseInfoCount))
  1244. ErrorExit(err);
  1245. if (err = LanmanUseAugment(num_read, NetUseInfoBuffer))
  1246. ErrorExit(err);
  1247. if (num_read == 0)
  1248. EmptyExit();
  1249. for (i = 0, pNetUseInfo = NetUseInfoBuffer;
  1250. i < num_read; i++, pNetUseInfo++)
  1251. {
  1252. //
  1253. // if we find at least one entry we will display, break
  1254. //
  1255. if (!(pNetUseInfo->fIsLanman)
  1256. || (pNetUseInfo->dwStatus != USE_OK)
  1257. || (pNetUseInfo->dwUseCount != 0)
  1258. || (pNetUseInfo->dwRefCount != 0))
  1259. break;
  1260. }
  1261. qsort(NetUseInfoBuffer,
  1262. num_read,
  1263. sizeof(NET_USE_INFO), CmpUseInfo);
  1264. if (i != num_read)
  1265. {
  1266. InfoPrint(APE_KillDevList);
  1267. for (i = 0, pNetUseInfo = NetUseInfoBuffer;
  1268. i < num_read;
  1269. i++, pNetUseInfo++)
  1270. {
  1271. if (pNetUseInfo->lpLocalName[0] != NULLC)
  1272. WriteToCon(TEXT(" %Fws %Fws\r\n"),
  1273. PaddedString(15,pNetUseInfo->lpLocalName,NULL),
  1274. pNetUseInfo->lpRemoteName);
  1275. else
  1276. WriteToCon(TEXT(" %Fws %Fws\r\n"),
  1277. PaddedString(15,pNetUseInfo->lpLocalName,NULL),
  1278. pNetUseInfo->lpRemoteName);
  1279. }
  1280. InfoPrint(APE_KillCancel);
  1281. if (!YorN(APE_ProceedWOp, 0))
  1282. NetcmdExit(2);
  1283. }
  1284. bConnectFlags = CONNECT_UPDATE_PROFILE ; // deletes always update
  1285. ulErr = NO_ERROR;
  1286. ulFirstErr = NO_ERROR;
  1287. for (i = 0, pNetUseInfo = NetUseInfoBuffer;
  1288. i < num_read;
  1289. i++, pNetUseInfo++)
  1290. {
  1291. /* delete both local and UNC uses */
  1292. if (pNetUseInfo->lpLocalName[0] != NULLC)
  1293. {
  1294. ulErr = WNetCancelConnection2( pNetUseInfo->lpLocalName,
  1295. bConnectFlags,
  1296. FALSE);
  1297. }
  1298. else
  1299. {
  1300. /*
  1301. * Delete All UNC uses to use_entry->ui1_remote
  1302. */
  1303. if ( pNetUseInfo->dwUseCount == 0 )
  1304. {
  1305. pNetUseInfo->dwUseCount = 1;
  1306. }
  1307. for( j = 0; j < pNetUseInfo->dwUseCount; j++ )
  1308. {
  1309. ulErr = WNetCancelConnection2( pNetUseInfo->lpRemoteName,
  1310. bConnectFlags,
  1311. FALSE );
  1312. if ( ulErr != NO_ERROR )
  1313. break;
  1314. }
  1315. }
  1316. switch(ulErr)
  1317. {
  1318. case NO_ERROR:
  1319. /* The use was returned by Enum, but is already gone */
  1320. case WN_BAD_NETNAME:
  1321. case WN_NOT_CONNECTED:
  1322. break;
  1323. case WN_OPEN_FILES:
  1324. if (pNetUseInfo->lpLocalName[0] != NULLC)
  1325. IStrings[0] = pNetUseInfo->lpLocalName;
  1326. else
  1327. IStrings[0] = pNetUseInfo->lpRemoteName;
  1328. InfoPrintIns(APE_OpenHandles, 1);
  1329. if (!YorN(APE_UseBlowAway, 0))
  1330. continue;
  1331. if (pNetUseInfo->lpLocalName[0] != NULLC)
  1332. {
  1333. ulErr = WNetCancelConnection2( pNetUseInfo->lpLocalName,
  1334. bConnectFlags,
  1335. TRUE ) ;
  1336. }
  1337. else
  1338. {
  1339. /*
  1340. * Delete All UNC uses to use_entry->ui1_remote
  1341. */
  1342. for( j = 0; j < pNetUseInfo->dwUseCount; j++ )
  1343. {
  1344. ulErr = WNetCancelConnection2( pNetUseInfo->lpRemoteName,
  1345. bConnectFlags,
  1346. TRUE );
  1347. if ( ulErr != NO_ERROR )
  1348. break;
  1349. }
  1350. }
  1351. if (ulErr == NO_ERROR)
  1352. break;
  1353. // fall through
  1354. default:
  1355. ulFirstErr = ulErr;
  1356. }
  1357. }
  1358. FreeMem((LPBYTE)NetUseInfoBuffer);
  1359. if (ulFirstErr != NO_ERROR)
  1360. WNetErrorExit( ulErr );
  1361. }
  1362. /***
  1363. * LanmanDisplayUse()
  1364. * Display info from a USE_INFO_1 struct
  1365. *
  1366. * Args:
  1367. * use_entry - pointer to a USE_INFO_1 struct
  1368. *
  1369. * Returns:
  1370. * 0
  1371. */
  1372. VOID
  1373. LanmanDisplayUse(
  1374. LPUSE_INFO_1 use_entry
  1375. )
  1376. {
  1377. DWORD maxLen;
  1378. USHORT status = APE2_GEN_UNKNOWN;
  1379. USHORT type = APE2_GEN_UNKNOWN;
  1380. switch(use_entry->ui1_asg_type)
  1381. {
  1382. case USE_DISKDEV:
  1383. type = APE2_USE_TYPE_DISK;
  1384. break;
  1385. case USE_SPOOLDEV:
  1386. type = APE2_USE_TYPE_PRINT;
  1387. break;
  1388. case USE_CHARDEV:
  1389. type = APE2_USE_TYPE_COMM;
  1390. break;
  1391. case USE_IPC:
  1392. type = APE2_USE_TYPE_IPC;
  1393. break;
  1394. }
  1395. UseMsgList[USE_TYPE_TBD].msg_number = type;
  1396. switch(use_entry->ui1_status)
  1397. {
  1398. case USE_OK:
  1399. status = APE2_USE_STATUS_OK;
  1400. break;
  1401. case USE_PAUSED:
  1402. status = APE2_USE_STATUS_PAUSED;
  1403. break;
  1404. case USE_SESSLOST:
  1405. status = APE2_USE_STATUS_SESSION_LOST;
  1406. break;
  1407. case USE_NETERR:
  1408. status = APE2_USE_STATUS_NET_ERROR;
  1409. break;
  1410. case USE_CONN:
  1411. status = APE2_USE_STATUS_CONNECTING;
  1412. break;
  1413. case USE_RECONN:
  1414. status = APE2_USE_STATUS_RECONNECTING;
  1415. break;
  1416. }
  1417. UseMsgList[USE_STATUS_TBD].msg_number = status;
  1418. GetMessageList(NUM_USE_MSGS, UseMsgList, &maxLen);
  1419. maxLen += 5;
  1420. WriteToCon(fmtPSZ,0,maxLen,
  1421. PaddedString(maxLen, UseMsgList[USE_MSG_LOCAL].msg_text, NULL),
  1422. use_entry->ui1_local);
  1423. WriteToCon(fmtPSZ,0,maxLen,
  1424. PaddedString(maxLen, UseMsgList[USE_MSG_REMOTE].msg_text, NULL),
  1425. use_entry->ui1_remote);
  1426. WriteToCon(fmtNPSZ,0,maxLen,
  1427. PaddedString(maxLen, UseMsgList[USE_MSG_TYPE].msg_text, NULL),
  1428. UseMsgList[USE_TYPE_TBD].msg_text);
  1429. WriteToCon(fmtNPSZ,0,maxLen,
  1430. PaddedString(maxLen, UseMsgList[USE_MSG_STATUS].msg_text, NULL),
  1431. UseMsgList[USE_STATUS_TBD].msg_text);
  1432. WriteToCon(fmtUSHORT,0,maxLen,
  1433. PaddedString(maxLen, UseMsgList[USE_MSG_OPEN_COUNT].msg_text, NULL),
  1434. use_entry->ui1_refcount);
  1435. WriteToCon(fmtUSHORT,0,maxLen,
  1436. PaddedString(maxLen, UseMsgList[USE_MSG_USE_COUNT].msg_text, NULL),
  1437. use_entry->ui1_usecount);
  1438. }
  1439. /***
  1440. * use_add_home()
  1441. * Add a use for the user's home directory
  1442. *
  1443. * Args:
  1444. * Dev - device to be used as the home directory
  1445. * Pass - password, or NULL if none supplied
  1446. *
  1447. * Returns:
  1448. * 0 - success
  1449. * exit(2) - command failed
  1450. */
  1451. void
  1452. use_add_home(
  1453. LPTSTR Dev,
  1454. LPTSTR Pass
  1455. )
  1456. {
  1457. DWORD dwErr;
  1458. TCHAR HomeDir[PATHLEN];
  1459. TCHAR Server[MAX_PATH + 1];
  1460. TCHAR FAR *SubPath;
  1461. ULONG Type;
  1462. TCHAR User[UNLEN + 1];
  1463. TCHAR *DeviceName = Dev ;
  1464. LPWKSTA_INFO_1 info_entry_w;
  1465. LPUSER_INFO_11 user_entry;
  1466. //
  1467. // If necessary, start the workstation and logon.
  1468. //
  1469. UseInit();
  1470. //
  1471. // Get the user name and the name of the server that logged the user on.
  1472. //
  1473. dwErr = MNetWkstaGetInfo(1, (LPBYTE*) &info_entry_w) ;
  1474. if (dwErr)
  1475. {
  1476. ErrorExit(dwErr);
  1477. }
  1478. _tcscpy(User, info_entry_w->wki1_username);
  1479. _tcscpy(Server, TEXT("\\\\")) ;
  1480. _tcscat(Server, info_entry_w->wki1_logon_server);
  1481. NetApiBufferFree((TCHAR FAR *) info_entry_w);
  1482. /* Now get user information (ie. the home directory). If you were */
  1483. /* logged on STANDALONE, and the local machine is a STANDALONE */
  1484. /* server, this will still work. Otherwise (eg., you are logged on */
  1485. /* STANDALONE on a DOS workstation), it will fail because there is */
  1486. /* no local UAS. */
  1487. dwErr = NetUserGetInfo(Server, User, 11, (LPBYTE *) &user_entry);
  1488. if (dwErr)
  1489. {
  1490. ErrorExit(APE_UseHomeDirNotDetermined);
  1491. }
  1492. _tcscpy(HomeDir, user_entry->usri11_home_dir);
  1493. NetApiBufferFree((TCHAR FAR *) user_entry);
  1494. /* If it is null string, return a "not set" error msg. */
  1495. if (HomeDir[0] == NULLC)
  1496. ErrorExit(APE_UseHomeDirNotSet);
  1497. /* Make sure the home directory is a UNC name. This does not */
  1498. /* insure that the sharename is usable under DOS, but the */
  1499. /* general policy on this issue is to make it the admin's */
  1500. /* responsibility to be aware of non-8.3 sharename implications, */
  1501. /* and "net share" does issue a warning. */
  1502. dwErr = I_NetPathType(NULL, HomeDir, &Type, 0L);
  1503. if (dwErr || Type != ITYPE_UNC)
  1504. ErrorExitInsTxt(APE_UseHomeDirNotUNC, HomeDir);
  1505. /* Split the home directory into a remote name and a subpath. */
  1506. /* After doing this, HomeDir points to the remote name, and */
  1507. /* SubPath points to a subpath, or a null string if there is */
  1508. /* no subpath. */
  1509. /* Find the backslash between the computername and the sharename. */
  1510. SubPath = _tcschr(HomeDir + 2, '\\');
  1511. /* Find the next backslash, if there is one. */
  1512. SubPath = _tcschr(SubPath + 1, '\\');
  1513. if (SubPath)
  1514. {
  1515. *SubPath = NULLC;
  1516. SubPath++;
  1517. }
  1518. else
  1519. SubPath = NULL_STRING;
  1520. /* Map the wild cards as need */
  1521. if (DeviceName)
  1522. {
  1523. DeviceName = MapWildCard(DeviceName, NULL) ;
  1524. if (!DeviceName)
  1525. {
  1526. // this can only happen if no drives left
  1527. ErrorExit(APE_UseWildCardNoneLeft) ;
  1528. }
  1529. }
  1530. /* Do the use. If we return, we succeeded. */
  1531. use_add(DeviceName, HomeDir, Pass, FALSE, FALSE);
  1532. IStrings[0] = DeviceName;
  1533. IStrings[1] = HomeDir;
  1534. IStrings[2] = DeviceName;
  1535. IStrings[3] = SubPath;
  1536. InfoPrintIns(APE_UseHomeDirSuccess, 4);
  1537. return;
  1538. }
  1539. int NEAR is_admin_dollar(TCHAR FAR * name)
  1540. {
  1541. TCHAR FAR * tfpC;
  1542. tfpC = _tcspbrk(name + 2, TEXT("\\/"));
  1543. if (tfpC == NULL)
  1544. return(0);
  1545. tfpC += 1;
  1546. return(!_tcsicmp(ADMIN_DOLLAR, tfpC));
  1547. }
  1548. /***
  1549. * UseInit()
  1550. * Common initialization processing for all the use.c module entry
  1551. * points.
  1552. *
  1553. * Args: None.
  1554. *
  1555. * Returns: None.
  1556. */
  1557. VOID NEAR
  1558. UseInit(VOID)
  1559. {
  1560. LanmanProviderName = GetLanmanProviderName() ;
  1561. if (LanmanProviderName == NULL)
  1562. LanmanProviderName = TEXT("") ;
  1563. }
  1564. /*
  1565. * query the user profile to see if connections are currently being remembered
  1566. */
  1567. USHORT QueryDefaultPersistence(BOOL *pfRemember)
  1568. {
  1569. // by adding the two, we are guaranteed to have enough
  1570. TCHAR szAnswer[(sizeof(MPR_YES_VALUE)+sizeof(MPR_NO_VALUE))/sizeof(TCHAR)] ;
  1571. ULONG iRes, len ;
  1572. len = DIMENSION(szAnswer) ;
  1573. iRes = GetProfileString(MPR_NETWORK_SECTION,
  1574. MPR_SAVECONNECTION_KEY,
  1575. MPR_YES_VALUE, // default is yes
  1576. szAnswer,
  1577. len) ;
  1578. if (iRes == len) // error
  1579. return(APE_ProfileReadError) ;
  1580. *pfRemember = (_tcsicmp(szAnswer,MPR_YES_VALUE)==0) ;
  1581. return (NERR_Success) ;
  1582. }
  1583. /*
  1584. * set if connections are currently being remembered
  1585. */
  1586. DWORD
  1587. SetDefaultPersistence(
  1588. BOOL fRemember
  1589. )
  1590. {
  1591. BOOL fSuccess ;
  1592. fSuccess = (WriteProfileString(MPR_NETWORK_SECTION,
  1593. MPR_SAVECONNECTION_KEY,
  1594. fRemember ? MPR_YES_VALUE : MPR_NO_VALUE ) != 0);
  1595. return (fSuccess ? NERR_Success : APE_ProfileWriteError) ;
  1596. }
  1597. /*
  1598. * WNetErrorExit()
  1599. * maps the Winnet error to NERR and error exits
  1600. *
  1601. * arguments: ULONG win32 error code.
  1602. * return value: none. this baby dont return
  1603. */
  1604. VOID
  1605. WNetErrorExit(
  1606. ULONG ulWNetErr
  1607. )
  1608. {
  1609. WCHAR w_ErrorText[256];
  1610. WCHAR w_ProviderText[64];
  1611. LONG ulExtendedError ;
  1612. DWORD err ;
  1613. switch (ulWNetErr)
  1614. {
  1615. case WN_SUCCESS :
  1616. return ;
  1617. case WN_BAD_POINTER :
  1618. case WN_BAD_VALUE :
  1619. err = ERROR_INVALID_PARAMETER ;
  1620. break ;
  1621. case WN_BAD_USER :
  1622. err = APE_BadUserContext ;
  1623. break ;
  1624. case WN_NO_NET_OR_BAD_PATH :
  1625. err = ERROR_BAD_NET_NAME ;
  1626. break ;
  1627. case WN_NO_NETWORK :
  1628. err = NERR_WkstaNotStarted ;
  1629. break ;
  1630. case WN_NOT_CONNECTED :
  1631. err = NERR_UseNotFound ;
  1632. break ;
  1633. case WN_DEVICE_IN_USE :
  1634. err = NERR_DevInUse ;
  1635. break ;
  1636. case WN_BAD_PROFILE :
  1637. case WN_CANNOT_OPEN_PROFILE :
  1638. err = APE_ProfileReadError ;
  1639. break ;
  1640. /*
  1641. * these should not happen under the calls we currently make,
  1642. * but for completeness, they are there.
  1643. */
  1644. case WN_BAD_PROVIDER :
  1645. case WN_CONNECTION_CLOSED :
  1646. case WN_NOT_CONTAINER :
  1647. case WN_FUNCTION_BUSY :
  1648. case WN_DEVICE_ERROR :
  1649. err = ERROR_UNEXP_NET_ERR ;
  1650. break ;
  1651. /*
  1652. * special case this one
  1653. */
  1654. case WN_EXTENDED_ERROR :
  1655. // get the extended error
  1656. ulWNetErr = WNetGetLastError(&ulExtendedError,
  1657. (LPWSTR)w_ErrorText,
  1658. DIMENSION(w_ErrorText),
  1659. (LPWSTR)w_ProviderText,
  1660. DIMENSION(w_ProviderText));
  1661. // if we got it, print it out
  1662. if (ulWNetErr == WN_SUCCESS)
  1663. {
  1664. TCHAR buf[16] ;
  1665. IStrings[0] = _ultow(ulExtendedError, buf, 10);
  1666. ErrorPrint(APE_OS2Error,1) ;
  1667. WriteToCon(TEXT("%ws\r\n"), w_ErrorText) ;
  1668. MyExit(2) ;
  1669. }
  1670. // otherwise report it as unexpected error
  1671. err = ERROR_UNEXP_NET_ERR ;
  1672. break ;
  1673. default:
  1674. // the the remainder dont need to be mapped.
  1675. err = ulWNetErr ;
  1676. break ;
  1677. }
  1678. ErrorExit(err) ;
  1679. }
  1680. /*
  1681. * code to handle the situation where the user has a remembered
  1682. * connection that is currently not used, and we need to figure out
  1683. * if we need to delete it first.
  1684. *
  1685. * returns whether we need update profile.
  1686. */
  1687. BOOL CheckIfWantUpdate(TCHAR *dev, TCHAR *resource)
  1688. {
  1689. WCHAR w_RemoteName[MAX_PATH];
  1690. ULONG ulErr, cchRemoteName = DIMENSION(w_RemoteName);
  1691. // if deviceless, no problem since never remembered anyway.
  1692. if (dev == NULL)
  1693. return FALSE ;
  1694. // check out the device
  1695. ulErr = WNetGetConnection( (LPWSTR)dev,
  1696. (LPWSTR)w_RemoteName,
  1697. &cchRemoteName );
  1698. // device is really connected, bag out
  1699. if (ulErr == WN_SUCCESS)
  1700. ErrorExit(ERROR_ALREADY_ASSIGNED) ;
  1701. // it is an unavail remembered device, so prompt as need
  1702. if (ulErr == WN_CONNECTION_CLOSED)
  1703. {
  1704. // if the new and old are the same we just return FALSE
  1705. // since the user effectively does not change his profile
  1706. if (!_tcsicmp(w_RemoteName, resource))
  1707. {
  1708. return FALSE ;
  1709. }
  1710. // check if YES/NO switch is specified.
  1711. if (YorN_Switch == 2)
  1712. {
  1713. // he specified /NO, so we tell him why we bag out
  1714. IStrings[0] = dev ;
  1715. IStrings[1] = w_RemoteName ;
  1716. ErrorExitIns(APE_DeviceIsRemembered,2) ;
  1717. }
  1718. // nothing specified, so ask user
  1719. if (YorN_Switch == 0)
  1720. {
  1721. IStrings[0] = dev ;
  1722. IStrings[1] = w_RemoteName ;
  1723. if (!LUI_YorNIns(IStrings,2,APE_OverwriteRemembered,1))
  1724. {
  1725. // he said no, so quit
  1726. NetcmdExit(2) ;
  1727. }
  1728. }
  1729. // remove the persistent connection,
  1730. // we get here if the user specifies /YES, or didnt
  1731. // specify anything but consented,
  1732. if (WNetCancelConnection2( dev,
  1733. CONNECT_UPDATE_PROFILE,
  1734. FALSE) != WN_SUCCESS)
  1735. ErrorExit(APE_ProfileWriteError) ;
  1736. }
  1737. // if we get here then all is cool, let the caller carry on.
  1738. return TRUE ;
  1739. }
  1740. #define PROVIDER_NAME_LEN 256
  1741. #define PROVIDER_NAME_VALUE L"Name"
  1742. #define PROVIDER_NAME_KEY L"System\\CurrentControlSet\\Services\\LanmanWorkstation\\NetworkProvider"
  1743. /***
  1744. * GetLanmanProviderName()
  1745. * Reads the Lanman provider name from the registry.
  1746. * This is to make sure we only use the LM provider even
  1747. * if someone else supports UNC.
  1748. *
  1749. * Args:
  1750. * none
  1751. *
  1752. * Returns:
  1753. * pointer to provider name if success
  1754. * NULL if cannot read registry
  1755. * ErrorExit() for other errors.
  1756. */
  1757. WCHAR *GetLanmanProviderName(void)
  1758. {
  1759. LONG Nerr;
  1760. LPBYTE buffer ;
  1761. HKEY hKey ;
  1762. DWORD buffersize, datatype ;
  1763. buffersize = PROVIDER_NAME_LEN * sizeof(WCHAR) ;
  1764. datatype = REG_SZ ;
  1765. if (Nerr = AllocMem(buffersize, &buffer))
  1766. ErrorExit(Nerr) ;
  1767. Nerr = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
  1768. PROVIDER_NAME_KEY,
  1769. 0L,
  1770. KEY_QUERY_VALUE,
  1771. &hKey) ;
  1772. if (Nerr != ERROR_SUCCESS)
  1773. {
  1774. // if cannot read, we use NULL. this is more generous
  1775. // than normal, but at least the command will still work if
  1776. // we cannot get to this.
  1777. return NULL ;
  1778. }
  1779. Nerr = RegQueryValueExW(hKey,
  1780. PROVIDER_NAME_VALUE,
  1781. 0L,
  1782. &datatype,
  1783. (LPBYTE)buffer,
  1784. &buffersize) ;
  1785. if (Nerr == ERROR_MORE_DATA)
  1786. {
  1787. if (Nerr = AllocMem(buffersize, &buffer))
  1788. {
  1789. RegCloseKey(hKey) ; // ignore any error here. its harmless
  1790. // and NET.EXE doesn't hang around.
  1791. ErrorExit(Nerr);
  1792. }
  1793. Nerr = RegQueryValueExW(hKey,
  1794. PROVIDER_NAME_VALUE,
  1795. 0L,
  1796. &datatype,
  1797. (LPBYTE)buffer,
  1798. &buffersize) ;
  1799. }
  1800. (void) RegCloseKey(hKey) ; // ignore any error here. its harmless
  1801. // and NET.EXE doesnt hang around anyway.
  1802. if (Nerr != ERROR_SUCCESS)
  1803. {
  1804. return(NULL) ; // treat as cannot read
  1805. }
  1806. return ((WCHAR *) buffer);
  1807. }
  1808. /***
  1809. * MapWildCard()
  1810. * Maps the wildcard ASTERISK to next avail drive.
  1811. *
  1812. * Args:
  1813. * dev - the input string. Must NOT be NULL.
  1814. *
  1815. * Returns:
  1816. * dev unchanged if it is not the wildcard
  1817. * pointer to next avail drive id dev is wildcard
  1818. * NULL if there are no avail drives left
  1819. */
  1820. LPTSTR
  1821. MapWildCard(
  1822. LPTSTR dev,
  1823. LPTSTR startdev
  1824. )
  1825. {
  1826. static TCHAR new_dev[DEVLEN+1] ;
  1827. //
  1828. // if not the wold card char, just return it unchanged
  1829. //
  1830. if (!IsWildCard(dev))
  1831. {
  1832. return dev ;
  1833. }
  1834. //
  1835. // need find the next avail drive letter
  1836. // note: the char advance does not need to be DBCS safe,
  1837. // since we are only dealing with drive letters.
  1838. //
  1839. if ( startdev != NULL )
  1840. {
  1841. _tcscpy(new_dev, startdev);
  1842. }
  1843. else
  1844. {
  1845. _tcscpy(new_dev,TEXT("Z:\\")) ;
  1846. }
  1847. while ( TRUE )
  1848. {
  1849. if (GetDriveType(new_dev) == 1) // 1 means root not found
  1850. {
  1851. //
  1852. // check if it's a remembered connection
  1853. //
  1854. DWORD status;
  1855. TCHAR remote_name[40]; // length doesn't matter since we
  1856. // check for WN_MORE_DATA
  1857. DWORD length = sizeof(remote_name)/sizeof(TCHAR);
  1858. new_dev[2] = 0 ;
  1859. status = WNetGetConnection(new_dev, remote_name, &length);
  1860. if (status == WN_CONNECTION_CLOSED ||
  1861. status == WN_MORE_DATA ||
  1862. status == WN_SUCCESS)
  1863. {
  1864. //
  1865. // it's a remembered connection; try the next drive
  1866. //
  1867. new_dev[2] = TEXT('\\');
  1868. }
  1869. else
  1870. {
  1871. return (new_dev) ;
  1872. }
  1873. }
  1874. if ( new_dev[0] == 'c' || new_dev[0] == 'C' )
  1875. {
  1876. break;
  1877. }
  1878. --new_dev[0] ;
  1879. }
  1880. //
  1881. // if we got here, there were no drives left
  1882. //
  1883. return NULL ;
  1884. }