Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2269 lines
63 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. BOOL is_admin_dollar(LPWSTR);
  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. //
  801. // pbuf should be sized to whichever one is larger
  802. //
  803. C_ASSERT(PWLEN <= CREDUI_MAX_PASSWORD_LENGTH);
  804. static TCHAR pbuf[CREDUI_MAX_PASSWORD_LENGTH + 1];
  805. static TCHAR UserBuffer[CREDUI_MAX_USERNAME_LENGTH + 1];
  806. static TCHAR ServerNameBuffer[MAX_PATH + 1] = {0};
  807. USHORT err; /* short return status */
  808. ULONG ulErr ; /* long return status */
  809. ULONG longtype; /* type field for I_NetPath */
  810. NETRESOURCEW netresource ;
  811. BOOL fRememberSwitch = FALSE ;
  812. BOOL fRemember = FALSE ;
  813. BOOL fSmartCard = FALSE;
  814. ULONG bConnectFlags = 0L ;
  815. LPWSTR pw_username = NULL;
  816. LPWSTR pw_pass = NULL;
  817. TCHAR *devicename = dev ;
  818. BOOL fExitCodeIsDrive = FALSE ;
  819. BOOL fSaveCred = FALSE;
  820. // unreferenced
  821. (void) comm ;
  822. UseInit();
  823. //
  824. // Build a non-UNC version of name to connect to.
  825. //
  826. if ( name != NULL ) {
  827. TCHAR *SlashPointer;
  828. //
  829. // Lob off the leading backslashes
  830. //
  831. if ( name[0] == '\\' && name[1] == '\\' ) {
  832. _tcsncpy( ServerNameBuffer, &name[2], MAX_PATH );
  833. } else {
  834. _tcsncpy( ServerNameBuffer, name, MAX_PATH );
  835. }
  836. //
  837. // Lob off the share name
  838. //
  839. SlashPointer = _tcschr( ServerNameBuffer, '\\');
  840. if ( SlashPointer != NULL ) {
  841. *SlashPointer = '\0';
  842. }
  843. } else {
  844. ServerNameBuffer[0] = '\0';
  845. }
  846. // make sure we clean this up
  847. AddToMemClearList(pbuf, sizeof(pbuf), FALSE) ;
  848. // deal with any wild card Device specification
  849. if (devicename)
  850. {
  851. // If the devicname is a '?', then the exit code should be the ASCII
  852. // value of the drive that we connect.
  853. if (IsQuestionMark(devicename))
  854. {
  855. fExitCodeIsDrive = TRUE;
  856. }
  857. devicename = MapWildCard(devicename, NULL) ;
  858. if (!devicename)
  859. {
  860. // this can omly happen if no drives left
  861. ErrorExit(APE_UseWildCardNoneLeft) ;
  862. }
  863. }
  864. /* Initialize netresource structure */
  865. netresource.lpProvider = NULL ;
  866. netresource.lpLocalName = NULL ;
  867. netresource.lpRemoteName = NULL ;
  868. netresource.dwType = 0L ;
  869. if (devicename)
  870. {
  871. if (I_NetPathType(NULL, devicename, &longtype, 0L))
  872. ErrorExit(APE_UnknDevType);
  873. /*
  874. * NOTE: I would haved LOVED to have used a switch statement here.
  875. * But since types are now LONGS, and the compiler doesn't support
  876. * long switch statements, we're stuck with multiple if's. Sorry.
  877. */
  878. if (longtype == ITYPE_DEVICE_DISK)
  879. netresource.dwType = RESOURCETYPE_DISK;
  880. else if (longtype == ITYPE_DEVICE_LPT)
  881. netresource.dwType = RESOURCETYPE_PRINT;
  882. else if (longtype == ITYPE_DEVICE_COM)
  883. netresource.dwType = RESOURCETYPE_PRINT;
  884. else
  885. ErrorExit(APE_UnknDevType);
  886. netresource.lpLocalName = devicename ;
  887. }
  888. else
  889. {
  890. netresource.dwType = RESOURCETYPE_ANY;
  891. netresource.lpLocalName = L"" ;
  892. }
  893. if( name != NULL )
  894. {
  895. netresource.lpRemoteName = name ;
  896. }
  897. {
  898. USHORT i;
  899. // Find out if the /USER or /PERSISTENT switches are used
  900. for (i = 0; SwitchList[i]; i++)
  901. {
  902. //
  903. // Handle the /PERSISTENT switch
  904. //
  905. if ( !(_tcsncmp(SwitchList[i],
  906. swtxt_SW_USE_PERSISTENT,
  907. _tcslen(swtxt_SW_USE_PERSISTENT))) )
  908. {
  909. LPTSTR ptr;
  910. DWORD res;
  911. DWORD answer;
  912. fRememberSwitch = TRUE;
  913. // find the colon separator
  914. if ((ptr = FindColon(SwitchList[i])) == NULL)
  915. {
  916. ErrorExit(APE_InvalidSwitchArg);
  917. }
  918. // parse string after colon for YES or NO
  919. if ((res = LUI_ParseYesNo(ptr,&answer)) != 0)
  920. {
  921. ErrorExitInsTxt(APE_CmdArgIllegal,SwitchList[i]);
  922. }
  923. fRemember = (answer == LUI_YES_VAL) ;
  924. //
  925. // Handle the /USER switch
  926. //
  927. }
  928. else if ( !(_tcsncmp(SwitchList[i],
  929. swtxt_SW_USE_USER,
  930. _tcslen(swtxt_SW_USE_USER))) )
  931. {
  932. PTCHAR ptr;
  933. // find the colon separator
  934. if ((ptr = FindColon(SwitchList[i])) == NULL)
  935. ErrorExit(APE_InvalidSwitchArg);
  936. pw_username = ptr;
  937. //
  938. // Handle the /SMARTCARD switch
  939. //
  940. } else if ( !(_tcscmp(SwitchList[i], swtxt_SW_USE_SMARTCARD ))) {
  941. fSmartCard = TRUE;
  942. //
  943. // Handle the /SAVECRED switch
  944. //
  945. } else if ( !(_tcscmp(SwitchList[i], swtxt_SW_USE_SAVECRED ))) {
  946. fSaveCred = TRUE;
  947. //
  948. // Handle the /Delete switch
  949. // (The parser really doesn't let this through.)
  950. //
  951. }
  952. else if ( !(_tcscmp(SwitchList[i], swtxt_SW_DELETE)) )
  953. {
  954. // what the heck? adding and deleting?
  955. ErrorExit(APE_ConflictingSwitches) ;
  956. }
  957. // ignore other switches
  958. }
  959. }
  960. // remember switch was not specified
  961. if (!fRememberSwitch)
  962. {
  963. if (QueryDefaultPersistence(&fRemember)!=NERR_Success)
  964. InfoPrint(APE_ProfileReadError);
  965. }
  966. //
  967. // /user and /savecred are mutually exclusive.
  968. // This is because the auth packages don't call cred man if the user name
  969. // is specified. Therefore, the auth package didn't pass the target info to cred man.
  970. // If there is no target info, the UI will save a server specific cred.
  971. //
  972. if ( pw_username != NULL && fSaveCred ) {
  973. ErrorExit(APE_ConflictingSwitches) ;
  974. }
  975. //
  976. // Handle /SMARTCARD switch.
  977. // Prompt for smart card credentials.
  978. //
  979. if ( fSmartCard ) {
  980. //
  981. // We don't know how to save smartcard creds
  982. //
  983. if ( fSaveCred ) {
  984. ErrorExit(APE_ConflictingSwitches) ;
  985. }
  986. //
  987. // If a user name was specified,
  988. // use it to select which smart card to use.
  989. //
  990. if ( pw_username != NULL ) {
  991. _tcsncpy( UserBuffer, pw_username, CREDUI_MAX_USERNAME_LENGTH );
  992. } else {
  993. UserBuffer[0] = '\0';
  994. }
  995. //
  996. // If password is specified,
  997. // use it as the default PIN
  998. if ( pass != NULL ) {
  999. // Consider "*" to be the same as "not specified"
  1000. if (! _tcscmp(pass, TEXT("*"))) {
  1001. pbuf[0] = '\0';
  1002. } else {
  1003. if (err = LUI_CanonPassword(pass)) {
  1004. ErrorExit(err);
  1005. }
  1006. _tcsncpy( pbuf, pass, CREDUI_MAX_PASSWORD_LENGTH );
  1007. }
  1008. } else {
  1009. pbuf[0] = '\0';
  1010. }
  1011. //
  1012. // Call the common UI.
  1013. //
  1014. // RtlZeroMemory( &UiInfo, sizeof(UiInfo) );
  1015. // UiInfo.dwVersion = 1;
  1016. ulErr = CredUICmdLinePromptForCredentialsW(
  1017. ServerNameBuffer, // Target name
  1018. NULL, // No context
  1019. NO_ERROR, // No authentication error
  1020. UserBuffer,
  1021. CREDUI_MAX_USERNAME_LENGTH,
  1022. pbuf,
  1023. CREDUI_MAX_PASSWORD_LENGTH,
  1024. NULL, // SaveFlag not allowed unless flag is specified
  1025. CREDUI_FLAGS_REQUIRE_SMARTCARD |
  1026. CREDUI_FLAGS_DO_NOT_PERSIST );
  1027. if ( ulErr != NO_ERROR ) {
  1028. ErrorExit(ulErr);
  1029. }
  1030. pw_username = UserBuffer;
  1031. pw_pass = pbuf;
  1032. //
  1033. // Handle cases where the password is specified on the command line.
  1034. //
  1035. } else if (pass) {
  1036. //
  1037. // We don't know how to save creds we don't prompt for
  1038. //
  1039. if ( fSaveCred ) {
  1040. ErrorExit(APE_ConflictingSwitches) ;
  1041. }
  1042. if (! _tcscmp(pass, TEXT("*")))
  1043. {
  1044. pass = pbuf;
  1045. IStrings[0] = name;
  1046. ReadPass(pass, PWLEN, 0, APE_UsePassPrompt, 1, FALSE);
  1047. if (err = LUI_CanonPassword(pass))
  1048. ErrorExit(err);
  1049. }
  1050. else
  1051. {
  1052. if (err = LUI_CanonPassword(pass))
  1053. ErrorExit(err);
  1054. }
  1055. pw_pass = pass;
  1056. }
  1057. //
  1058. // Loop handling assigning to the next drive letter
  1059. //
  1060. do {
  1061. //if persistent, the check for clash with existing remembered connection
  1062. bConnectFlags = 0 ;
  1063. if (fRemember)
  1064. {
  1065. if (CheckIfWantUpdate(devicename, name))
  1066. bConnectFlags |= CONNECT_UPDATE_PROFILE ;
  1067. }
  1068. //
  1069. // Allow it to prompt for us if the credential aren't on the command line
  1070. //
  1071. if ( !pass && !fSmartCard) {
  1072. bConnectFlags |= CONNECT_INTERACTIVE | CONNECT_COMMANDLINE;
  1073. //
  1074. // If the caller wants to save both username and password,
  1075. // create an enterprise peristed cred.
  1076. //
  1077. if ( fSaveCred ) {
  1078. bConnectFlags |= CONNECT_CMD_SAVECRED;
  1079. }
  1080. }
  1081. ulErr = WNetAddConnection2(&netresource,
  1082. pw_pass,
  1083. pw_username,
  1084. bConnectFlags) ;
  1085. switch(ulErr)
  1086. {
  1087. case WN_SUCCESS:
  1088. if (fRememberSwitch)
  1089. {
  1090. DWORD dwErr = SetDefaultPersistence(fRemember);
  1091. if (dwErr != NERR_Success)
  1092. {
  1093. ErrorExit(dwErr);
  1094. }
  1095. }
  1096. if (print_ok)
  1097. {
  1098. if (IsWildCard(dev)) // if originally a wildcard
  1099. {
  1100. IStrings[0] = devicename;
  1101. IStrings[1] = name;
  1102. InfoPrintIns(APE_UseWildCardSuccess, 2) ;
  1103. }
  1104. InfoSuccess();
  1105. }
  1106. if (fExitCodeIsDrive)
  1107. {
  1108. MyExit((int)devicename[0]);
  1109. }
  1110. return;
  1111. case WN_BAD_PASSWORD:
  1112. case WN_ACCESS_DENIED:
  1113. case ERROR_LOGON_FAILURE:
  1114. WNetErrorExit(ulErr);
  1115. case ERROR_ALREADY_ASSIGNED:
  1116. if (!IsWildCard(dev))
  1117. {
  1118. ErrorExit(ERROR_ALREADY_ASSIGNED);
  1119. }
  1120. // Get another drive letter
  1121. (devicename[0])++;
  1122. devicename = MapWildCard(TEXT("*"), devicename) ;
  1123. if (!devicename)
  1124. {
  1125. // this can only happen if no drives left
  1126. ErrorExit(APE_UseWildCardNoneLeft) ;
  1127. }
  1128. netresource.lpLocalName = devicename ;
  1129. break;
  1130. case WN_BAD_NETNAME:
  1131. if (is_admin_dollar(name))
  1132. {
  1133. ErrorExit(APE_BadAdminConfig);
  1134. }
  1135. // else drop thru
  1136. default:
  1137. WNetErrorExit(ulErr);
  1138. }
  1139. } while ( ulErr == ERROR_ALREADY_ASSIGNED );
  1140. }
  1141. VOID
  1142. use_set_remembered(
  1143. VOID
  1144. )
  1145. {
  1146. PTCHAR ptr;
  1147. BOOL fRemember ;
  1148. // Find the /persistent switch
  1149. if ((ptr = FindColon(SwitchList[0])) == NULL)
  1150. ErrorExit(APE_InvalidSwitchArg);
  1151. if ( !(_tcscmp(SwitchList[0], swtxt_SW_USE_PERSISTENT) ) )
  1152. {
  1153. DWORD res;
  1154. DWORD answer;
  1155. if ((res = LUI_ParseYesNo(ptr,&answer)) != 0)
  1156. {
  1157. ErrorExitInsTxt(APE_CmdArgIllegal,SwitchList[0]) ;
  1158. }
  1159. fRemember = (answer == LUI_YES_VAL) ;
  1160. res = SetDefaultPersistence(fRemember);
  1161. if (res != NERR_Success)
  1162. {
  1163. ErrorExit(res);
  1164. }
  1165. }
  1166. else
  1167. {
  1168. ErrorExit(APE_InvalidSwitch);
  1169. }
  1170. InfoSuccess();
  1171. }
  1172. /***
  1173. * use_del()
  1174. * Delete a redirection
  1175. *
  1176. * Args:
  1177. * dev - device OR unc-name to delete
  1178. * print_ok - print success message?
  1179. *
  1180. * Returns:
  1181. * 0 - success
  1182. * exit(2) - command failed
  1183. */
  1184. VOID use_del(TCHAR * dev, BOOL deviceless, int print_ok)
  1185. {
  1186. ULONG ulErr; /* API return status */
  1187. BOOL fRememberSwitch = FALSE ;
  1188. ULONG bConnectFlags = 0L ;
  1189. USHORT i;
  1190. UseInit();
  1191. // Find out if the /PERSISTENT switch is used
  1192. for (i = 0; SwitchList[i]; i++)
  1193. {
  1194. if ( !(_tcscmp(SwitchList[i], swtxt_SW_USE_PERSISTENT)) )
  1195. ErrorExit(APE_InvalidSwitch);
  1196. }
  1197. if (IsWildCard(dev))
  1198. {
  1199. use_del_all() ;
  1200. goto gone;
  1201. }
  1202. bConnectFlags = CONNECT_UPDATE_PROFILE ; // deletes always update
  1203. if ((ulErr = WNetCancelConnection2( dev,
  1204. bConnectFlags,
  1205. FALSE)) != WN_SUCCESS)
  1206. {
  1207. if (ulErr != WN_OPEN_FILES)
  1208. WNetErrorExit(ulErr);
  1209. }
  1210. else
  1211. goto gone;
  1212. InfoPrintInsTxt(APE_OpenHandles, dev);
  1213. if (!YorN(APE_UseBlowAway, 0))
  1214. NetcmdExit(2);
  1215. if ((ulErr = WNetCancelConnection2( (LPWSTR)dev,
  1216. bConnectFlags,
  1217. TRUE )) != WN_SUCCESS)
  1218. WNetErrorExit(ulErr);
  1219. gone:
  1220. if (print_ok)
  1221. if ( IsWildCard( dev ) )
  1222. InfoSuccess();
  1223. else
  1224. InfoPrintInsTxt(APE_DelSuccess, dev);
  1225. }
  1226. /***
  1227. * use_del_all()
  1228. * Delete all redirections
  1229. *
  1230. * Args:
  1231. * none
  1232. *
  1233. * Returns:
  1234. * 0 - success
  1235. * exit(2) - command failed
  1236. */
  1237. VOID use_del_all()
  1238. {
  1239. DWORD err = 0; /* API return status */
  1240. ULONG ulErr = 0; /* WNet error */
  1241. ULONG ulFirstErr = 0; /* WNet error */
  1242. DWORD num_read = 0; /* num entries read by API */
  1243. DWORD i = 0,j = 0;
  1244. DWORD NetUseInfoCount = 0 ;
  1245. NET_USE_INFO *NetUseInfoBuffer = NULL ;
  1246. NET_USE_INFO *pNetUseInfo = NULL;
  1247. ULONG bConnectFlags = 0L ;
  1248. UseInit();
  1249. if (err = MprUseEnum(&num_read, &NetUseInfoBuffer, &NetUseInfoCount))
  1250. ErrorExit(err);
  1251. if (err = UnavailUseAugment(&num_read, &NetUseInfoBuffer, &NetUseInfoCount))
  1252. ErrorExit(err);
  1253. if (err = LanmanUseAugment(num_read, NetUseInfoBuffer))
  1254. ErrorExit(err);
  1255. if (num_read == 0)
  1256. EmptyExit();
  1257. for (i = 0, pNetUseInfo = NetUseInfoBuffer;
  1258. i < num_read; i++, pNetUseInfo++)
  1259. {
  1260. //
  1261. // if we find at least one entry we will display, break
  1262. //
  1263. if (!(pNetUseInfo->fIsLanman)
  1264. || (pNetUseInfo->dwStatus != USE_OK)
  1265. || (pNetUseInfo->dwUseCount != 0)
  1266. || (pNetUseInfo->dwRefCount != 0))
  1267. break;
  1268. }
  1269. qsort(NetUseInfoBuffer,
  1270. num_read,
  1271. sizeof(NET_USE_INFO), CmpUseInfo);
  1272. if (i != num_read)
  1273. {
  1274. InfoPrint(APE_KillDevList);
  1275. for (i = 0, pNetUseInfo = NetUseInfoBuffer;
  1276. i < num_read;
  1277. i++, pNetUseInfo++)
  1278. {
  1279. if (pNetUseInfo->lpLocalName[0] != NULLC)
  1280. WriteToCon(TEXT(" %Fws %Fws\r\n"),
  1281. PaddedString(15,pNetUseInfo->lpLocalName,NULL),
  1282. pNetUseInfo->lpRemoteName);
  1283. else
  1284. WriteToCon(TEXT(" %Fws %Fws\r\n"),
  1285. PaddedString(15,pNetUseInfo->lpLocalName,NULL),
  1286. pNetUseInfo->lpRemoteName);
  1287. }
  1288. InfoPrint(APE_KillCancel);
  1289. if (!YorN(APE_ProceedWOp, 0))
  1290. NetcmdExit(2);
  1291. }
  1292. bConnectFlags = CONNECT_UPDATE_PROFILE ; // deletes always update
  1293. ulErr = NO_ERROR;
  1294. ulFirstErr = NO_ERROR;
  1295. for (i = 0, pNetUseInfo = NetUseInfoBuffer;
  1296. i < num_read;
  1297. i++, pNetUseInfo++)
  1298. {
  1299. /* delete both local and UNC uses */
  1300. if (pNetUseInfo->lpLocalName[0] != NULLC)
  1301. {
  1302. ulErr = WNetCancelConnection2( pNetUseInfo->lpLocalName,
  1303. bConnectFlags,
  1304. FALSE);
  1305. }
  1306. else
  1307. {
  1308. /*
  1309. * Delete All UNC uses to use_entry->ui1_remote
  1310. */
  1311. if ( pNetUseInfo->dwUseCount == 0 )
  1312. {
  1313. pNetUseInfo->dwUseCount = 1;
  1314. }
  1315. for( j = 0; j < pNetUseInfo->dwUseCount; j++ )
  1316. {
  1317. ulErr = WNetCancelConnection2( pNetUseInfo->lpRemoteName,
  1318. bConnectFlags,
  1319. FALSE );
  1320. if ( ulErr != NO_ERROR )
  1321. break;
  1322. }
  1323. }
  1324. switch(ulErr)
  1325. {
  1326. case NO_ERROR:
  1327. /* The use was returned by Enum, but is already gone */
  1328. case WN_BAD_NETNAME:
  1329. case WN_NOT_CONNECTED:
  1330. break;
  1331. case WN_OPEN_FILES:
  1332. if (pNetUseInfo->lpLocalName[0] != NULLC)
  1333. IStrings[0] = pNetUseInfo->lpLocalName;
  1334. else
  1335. IStrings[0] = pNetUseInfo->lpRemoteName;
  1336. InfoPrintIns(APE_OpenHandles, 1);
  1337. if (!YorN(APE_UseBlowAway, 0))
  1338. continue;
  1339. if (pNetUseInfo->lpLocalName[0] != NULLC)
  1340. {
  1341. ulErr = WNetCancelConnection2( pNetUseInfo->lpLocalName,
  1342. bConnectFlags,
  1343. TRUE ) ;
  1344. }
  1345. else
  1346. {
  1347. /*
  1348. * Delete All UNC uses to use_entry->ui1_remote
  1349. */
  1350. for( j = 0; j < pNetUseInfo->dwUseCount; j++ )
  1351. {
  1352. ulErr = WNetCancelConnection2( pNetUseInfo->lpRemoteName,
  1353. bConnectFlags,
  1354. TRUE );
  1355. if ( ulErr != NO_ERROR )
  1356. break;
  1357. }
  1358. }
  1359. if (ulErr == NO_ERROR)
  1360. break;
  1361. // fall through
  1362. default:
  1363. ulFirstErr = ulErr;
  1364. }
  1365. }
  1366. FreeMem((LPBYTE)NetUseInfoBuffer);
  1367. if (ulFirstErr != NO_ERROR)
  1368. WNetErrorExit( ulErr );
  1369. }
  1370. /***
  1371. * LanmanDisplayUse()
  1372. * Display info from a USE_INFO_1 struct
  1373. *
  1374. * Args:
  1375. * use_entry - pointer to a USE_INFO_1 struct
  1376. *
  1377. * Returns:
  1378. * 0
  1379. */
  1380. VOID
  1381. LanmanDisplayUse(
  1382. LPUSE_INFO_1 use_entry
  1383. )
  1384. {
  1385. DWORD maxLen;
  1386. USHORT status = APE2_GEN_UNKNOWN;
  1387. USHORT type = APE2_GEN_UNKNOWN;
  1388. switch(use_entry->ui1_asg_type)
  1389. {
  1390. case USE_DISKDEV:
  1391. type = APE2_USE_TYPE_DISK;
  1392. break;
  1393. case USE_SPOOLDEV:
  1394. type = APE2_USE_TYPE_PRINT;
  1395. break;
  1396. case USE_CHARDEV:
  1397. type = APE2_USE_TYPE_COMM;
  1398. break;
  1399. case USE_IPC:
  1400. type = APE2_USE_TYPE_IPC;
  1401. break;
  1402. }
  1403. UseMsgList[USE_TYPE_TBD].msg_number = type;
  1404. switch(use_entry->ui1_status)
  1405. {
  1406. case USE_OK:
  1407. status = APE2_USE_STATUS_OK;
  1408. break;
  1409. case USE_PAUSED:
  1410. status = APE2_USE_STATUS_PAUSED;
  1411. break;
  1412. case USE_SESSLOST:
  1413. status = APE2_USE_STATUS_SESSION_LOST;
  1414. break;
  1415. case USE_NETERR:
  1416. status = APE2_USE_STATUS_NET_ERROR;
  1417. break;
  1418. case USE_CONN:
  1419. status = APE2_USE_STATUS_CONNECTING;
  1420. break;
  1421. case USE_RECONN:
  1422. status = APE2_USE_STATUS_RECONNECTING;
  1423. break;
  1424. }
  1425. UseMsgList[USE_STATUS_TBD].msg_number = status;
  1426. GetMessageList(NUM_USE_MSGS, UseMsgList, &maxLen);
  1427. maxLen += 5;
  1428. WriteToCon(fmtPSZ,0,maxLen,
  1429. PaddedString(maxLen, UseMsgList[USE_MSG_LOCAL].msg_text, NULL),
  1430. use_entry->ui1_local);
  1431. WriteToCon(fmtPSZ,0,maxLen,
  1432. PaddedString(maxLen, UseMsgList[USE_MSG_REMOTE].msg_text, NULL),
  1433. use_entry->ui1_remote);
  1434. WriteToCon(fmtNPSZ,0,maxLen,
  1435. PaddedString(maxLen, UseMsgList[USE_MSG_TYPE].msg_text, NULL),
  1436. UseMsgList[USE_TYPE_TBD].msg_text);
  1437. WriteToCon(fmtNPSZ,0,maxLen,
  1438. PaddedString(maxLen, UseMsgList[USE_MSG_STATUS].msg_text, NULL),
  1439. UseMsgList[USE_STATUS_TBD].msg_text);
  1440. WriteToCon(fmtUSHORT,0,maxLen,
  1441. PaddedString(maxLen, UseMsgList[USE_MSG_OPEN_COUNT].msg_text, NULL),
  1442. use_entry->ui1_refcount);
  1443. WriteToCon(fmtUSHORT,0,maxLen,
  1444. PaddedString(maxLen, UseMsgList[USE_MSG_USE_COUNT].msg_text, NULL),
  1445. use_entry->ui1_usecount);
  1446. }
  1447. /***
  1448. * use_add_home()
  1449. * Add a use for the user's home directory
  1450. *
  1451. * Args:
  1452. * Dev - device to be used as the home directory
  1453. * Pass - password, or NULL if none supplied
  1454. *
  1455. * Returns:
  1456. * 0 - success
  1457. * exit(2) - command failed
  1458. */
  1459. void
  1460. use_add_home(
  1461. LPTSTR Dev,
  1462. LPTSTR Pass
  1463. )
  1464. {
  1465. DWORD dwErr;
  1466. TCHAR HomeDir[PATHLEN];
  1467. TCHAR Server[MAX_PATH + 1];
  1468. TCHAR FAR *SubPath;
  1469. ULONG Type;
  1470. TCHAR User[UNLEN + 1];
  1471. TCHAR *DeviceName = Dev ;
  1472. LPWKSTA_INFO_1 info_entry_w;
  1473. LPUSER_INFO_11 user_entry;
  1474. //
  1475. // If necessary, start the workstation and logon.
  1476. //
  1477. UseInit();
  1478. //
  1479. // Get the user name and the name of the server that logged the user on.
  1480. //
  1481. dwErr = MNetWkstaGetInfo(1, (LPBYTE*) &info_entry_w) ;
  1482. if (dwErr)
  1483. {
  1484. ErrorExit(dwErr);
  1485. }
  1486. _tcscpy(User, info_entry_w->wki1_username);
  1487. _tcscpy(Server, TEXT("\\\\")) ;
  1488. _tcscat(Server, info_entry_w->wki1_logon_server);
  1489. NetApiBufferFree((TCHAR FAR *) info_entry_w);
  1490. /* Now get user information (ie. the home directory). If you were */
  1491. /* logged on STANDALONE, and the local machine is a STANDALONE */
  1492. /* server, this will still work. Otherwise (eg., you are logged on */
  1493. /* STANDALONE on a DOS workstation), it will fail because there is */
  1494. /* no local UAS. */
  1495. dwErr = NetUserGetInfo(Server, User, 11, (LPBYTE *) &user_entry);
  1496. if (dwErr)
  1497. {
  1498. ErrorExit(APE_UseHomeDirNotDetermined);
  1499. }
  1500. _tcscpy(HomeDir, user_entry->usri11_home_dir);
  1501. NetApiBufferFree((TCHAR FAR *) user_entry);
  1502. /* If it is null string, return a "not set" error msg. */
  1503. if (HomeDir[0] == NULLC)
  1504. ErrorExit(APE_UseHomeDirNotSet);
  1505. /* Make sure the home directory is a UNC name. This does not */
  1506. /* insure that the sharename is usable under DOS, but the */
  1507. /* general policy on this issue is to make it the admin's */
  1508. /* responsibility to be aware of non-8.3 sharename implications, */
  1509. /* and "net share" does issue a warning. */
  1510. dwErr = I_NetPathType(NULL, HomeDir, &Type, 0L);
  1511. if (dwErr || Type != ITYPE_UNC)
  1512. ErrorExitInsTxt(APE_UseHomeDirNotUNC, HomeDir);
  1513. /* Split the home directory into a remote name and a subpath. */
  1514. /* After doing this, HomeDir points to the remote name, and */
  1515. /* SubPath points to a subpath, or a null string if there is */
  1516. /* no subpath. */
  1517. /* Find the backslash between the computername and the sharename. */
  1518. SubPath = _tcschr(HomeDir + 2, '\\');
  1519. /* Find the next backslash, if there is one. */
  1520. SubPath = _tcschr(SubPath + 1, '\\');
  1521. if (SubPath)
  1522. {
  1523. *SubPath = NULLC;
  1524. SubPath++;
  1525. }
  1526. else
  1527. SubPath = NULL_STRING;
  1528. /* Map the wild cards as need */
  1529. if (DeviceName)
  1530. {
  1531. DeviceName = MapWildCard(DeviceName, NULL) ;
  1532. if (!DeviceName)
  1533. {
  1534. // this can only happen if no drives left
  1535. ErrorExit(APE_UseWildCardNoneLeft) ;
  1536. }
  1537. }
  1538. /* Do the use. If we return, we succeeded. */
  1539. use_add(DeviceName, HomeDir, Pass, FALSE, FALSE);
  1540. IStrings[0] = DeviceName;
  1541. IStrings[1] = HomeDir;
  1542. IStrings[2] = DeviceName;
  1543. IStrings[3] = SubPath;
  1544. InfoPrintIns(APE_UseHomeDirSuccess, 4);
  1545. return;
  1546. }
  1547. BOOL
  1548. is_admin_dollar(
  1549. LPWSTR name
  1550. )
  1551. {
  1552. LPWSTR tfpC;
  1553. if (name[0] == L'\0' || name[1] == L'\0')
  1554. {
  1555. return FALSE;
  1556. }
  1557. tfpC = _tcspbrk(name + 2, TEXT("\\/"));
  1558. if (tfpC == NULL)
  1559. {
  1560. return FALSE;
  1561. }
  1562. tfpC += 1;
  1563. return (!_tcsicmp(ADMIN_DOLLAR, tfpC));
  1564. }
  1565. /***
  1566. * UseInit()
  1567. * Common initialization processing for all the use.c module entry
  1568. * points.
  1569. *
  1570. * Args: None.
  1571. *
  1572. * Returns: None.
  1573. */
  1574. VOID NEAR
  1575. UseInit(VOID)
  1576. {
  1577. LanmanProviderName = GetLanmanProviderName() ;
  1578. if (LanmanProviderName == NULL)
  1579. {
  1580. LanmanProviderName = TEXT("") ;
  1581. }
  1582. }
  1583. /*
  1584. * query the user profile to see if connections are currently being remembered
  1585. */
  1586. USHORT QueryDefaultPersistence(BOOL *pfRemember)
  1587. {
  1588. // by adding the two, we are guaranteed to have enough
  1589. TCHAR szAnswer[(sizeof(MPR_YES_VALUE)+sizeof(MPR_NO_VALUE))/sizeof(TCHAR)] ;
  1590. ULONG iRes, len ;
  1591. len = DIMENSION(szAnswer) ;
  1592. iRes = GetProfileString(MPR_NETWORK_SECTION,
  1593. MPR_SAVECONNECTION_KEY,
  1594. MPR_YES_VALUE, // default is yes
  1595. szAnswer,
  1596. len) ;
  1597. if (iRes == len) // error
  1598. return(APE_ProfileReadError) ;
  1599. *pfRemember = (_tcsicmp(szAnswer,MPR_YES_VALUE)==0) ;
  1600. return (NERR_Success) ;
  1601. }
  1602. /*
  1603. * set if connections are currently being remembered
  1604. */
  1605. DWORD
  1606. SetDefaultPersistence(
  1607. BOOL fRemember
  1608. )
  1609. {
  1610. BOOL fSuccess ;
  1611. fSuccess = (WriteProfileString(MPR_NETWORK_SECTION,
  1612. MPR_SAVECONNECTION_KEY,
  1613. fRemember ? MPR_YES_VALUE : MPR_NO_VALUE ) != 0);
  1614. return (fSuccess ? NERR_Success : APE_ProfileWriteError) ;
  1615. }
  1616. /*
  1617. * WNetErrorExit()
  1618. * maps the Winnet error to NERR and error exits
  1619. *
  1620. * arguments: ULONG win32 error code.
  1621. * return value: none. this baby dont return
  1622. */
  1623. VOID
  1624. WNetErrorExit(
  1625. ULONG ulWNetErr
  1626. )
  1627. {
  1628. WCHAR w_ErrorText[256];
  1629. WCHAR w_ProviderText[64];
  1630. LONG ulExtendedError ;
  1631. DWORD err ;
  1632. switch (ulWNetErr)
  1633. {
  1634. case WN_SUCCESS :
  1635. return ;
  1636. case WN_BAD_POINTER :
  1637. case WN_BAD_VALUE :
  1638. err = ERROR_INVALID_PARAMETER ;
  1639. break ;
  1640. case WN_BAD_USER :
  1641. err = APE_BadUserContext ;
  1642. break ;
  1643. case WN_NO_NET_OR_BAD_PATH :
  1644. err = ERROR_BAD_NET_NAME ;
  1645. break ;
  1646. case WN_NO_NETWORK :
  1647. err = NERR_WkstaNotStarted ;
  1648. break ;
  1649. case WN_NOT_CONNECTED :
  1650. err = NERR_UseNotFound ;
  1651. break ;
  1652. case WN_DEVICE_IN_USE :
  1653. err = NERR_DevInUse ;
  1654. break ;
  1655. case WN_BAD_PROFILE :
  1656. case WN_CANNOT_OPEN_PROFILE :
  1657. err = APE_ProfileReadError ;
  1658. break ;
  1659. /*
  1660. * these should not happen under the calls we currently make,
  1661. * but for completeness, they are there.
  1662. */
  1663. case WN_BAD_PROVIDER :
  1664. case WN_CONNECTION_CLOSED :
  1665. case WN_NOT_CONTAINER :
  1666. case WN_FUNCTION_BUSY :
  1667. case WN_DEVICE_ERROR :
  1668. err = ERROR_UNEXP_NET_ERR ;
  1669. break ;
  1670. /*
  1671. * special case this one
  1672. */
  1673. case WN_EXTENDED_ERROR :
  1674. // get the extended error
  1675. ulWNetErr = WNetGetLastError(&ulExtendedError,
  1676. (LPWSTR)w_ErrorText,
  1677. DIMENSION(w_ErrorText),
  1678. (LPWSTR)w_ProviderText,
  1679. DIMENSION(w_ProviderText));
  1680. // if we got it, print it out
  1681. if (ulWNetErr == WN_SUCCESS)
  1682. {
  1683. TCHAR buf[40];
  1684. IStrings[0] = _ultow(ulExtendedError, buf, 10);
  1685. ErrorPrint(APE_OS2Error,1) ;
  1686. WriteToCon(TEXT("%ws\r\n"), w_ErrorText) ;
  1687. MyExit(2) ;
  1688. }
  1689. // otherwise report it as unexpected error
  1690. err = ERROR_UNEXP_NET_ERR ;
  1691. break ;
  1692. default:
  1693. // the the remainder dont need to be mapped.
  1694. err = ulWNetErr ;
  1695. break ;
  1696. }
  1697. ErrorExit(err) ;
  1698. }
  1699. /*
  1700. * code to handle the situation where the user has a remembered
  1701. * connection that is currently not used, and we need to figure out
  1702. * if we need to delete it first.
  1703. *
  1704. * returns whether we need update profile.
  1705. */
  1706. BOOL CheckIfWantUpdate(TCHAR *dev, TCHAR *resource)
  1707. {
  1708. WCHAR w_RemoteName[MAX_PATH];
  1709. ULONG ulErr, cchRemoteName = DIMENSION(w_RemoteName);
  1710. // if deviceless, no problem since never remembered anyway.
  1711. if (dev == NULL)
  1712. return FALSE ;
  1713. // check out the device
  1714. ulErr = WNetGetConnection( (LPWSTR)dev,
  1715. (LPWSTR)w_RemoteName,
  1716. &cchRemoteName );
  1717. // device is really connected, bag out
  1718. if (ulErr == WN_SUCCESS)
  1719. ErrorExit(ERROR_ALREADY_ASSIGNED) ;
  1720. // it is an unavail remembered device, so prompt as need
  1721. if (ulErr == WN_CONNECTION_CLOSED)
  1722. {
  1723. // if the new and old are the same we just return FALSE
  1724. // since the user effectively does not change his profile
  1725. if (!_tcsicmp(w_RemoteName, resource))
  1726. {
  1727. return FALSE ;
  1728. }
  1729. // check if YES/NO switch is specified.
  1730. if (YorN_Switch == 2)
  1731. {
  1732. // he specified /NO, so we tell him why we bag out
  1733. IStrings[0] = dev ;
  1734. IStrings[1] = w_RemoteName ;
  1735. ErrorExitIns(APE_DeviceIsRemembered,2) ;
  1736. }
  1737. // nothing specified, so ask user
  1738. if (YorN_Switch == 0)
  1739. {
  1740. IStrings[0] = dev ;
  1741. IStrings[1] = w_RemoteName ;
  1742. if (!LUI_YorNIns(IStrings,2,APE_OverwriteRemembered,1))
  1743. {
  1744. // he said no, so quit
  1745. NetcmdExit(2) ;
  1746. }
  1747. }
  1748. // remove the persistent connection,
  1749. // we get here if the user specifies /YES, or didnt
  1750. // specify anything but consented,
  1751. if (WNetCancelConnection2( dev,
  1752. CONNECT_UPDATE_PROFILE,
  1753. FALSE) != WN_SUCCESS)
  1754. ErrorExit(APE_ProfileWriteError) ;
  1755. }
  1756. // if we get here then all is cool, let the caller carry on.
  1757. return TRUE ;
  1758. }
  1759. #define PROVIDER_NAME_LEN 256
  1760. #define PROVIDER_NAME_VALUE L"Name"
  1761. #define PROVIDER_NAME_KEY L"System\\CurrentControlSet\\Services\\LanmanWorkstation\\NetworkProvider"
  1762. /***
  1763. * GetLanmanProviderName()
  1764. * Reads the Lanman provider name from the registry.
  1765. * This is to make sure we only use the LM provider even
  1766. * if someone else supports UNC.
  1767. *
  1768. * Args:
  1769. * none
  1770. *
  1771. * Returns:
  1772. * pointer to provider name if success
  1773. * NULL if cannot read registry
  1774. * ErrorExit() for other errors.
  1775. */
  1776. WCHAR *GetLanmanProviderName(void)
  1777. {
  1778. LONG Nerr;
  1779. LPBYTE buffer ;
  1780. HKEY hKey ;
  1781. DWORD buffersize, datatype ;
  1782. buffersize = PROVIDER_NAME_LEN * sizeof(WCHAR) ;
  1783. datatype = REG_SZ ;
  1784. if (Nerr = AllocMem(buffersize, &buffer))
  1785. {
  1786. ErrorExit(Nerr);
  1787. }
  1788. Nerr = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
  1789. PROVIDER_NAME_KEY,
  1790. 0L,
  1791. KEY_QUERY_VALUE,
  1792. &hKey) ;
  1793. if (Nerr != ERROR_SUCCESS)
  1794. {
  1795. //
  1796. // if cannot read, we use NULL. this is more generous
  1797. // than normal, but at least the command will still work if
  1798. // we cannot get to this.
  1799. //
  1800. FreeMem(buffer);
  1801. return NULL;
  1802. }
  1803. Nerr = RegQueryValueExW(hKey,
  1804. PROVIDER_NAME_VALUE,
  1805. 0L,
  1806. &datatype,
  1807. (LPBYTE)buffer,
  1808. &buffersize) ;
  1809. if (Nerr == ERROR_MORE_DATA)
  1810. {
  1811. if (Nerr = ReallocMem(buffersize, &buffer))
  1812. {
  1813. RegCloseKey(hKey);
  1814. ErrorExit(Nerr);
  1815. }
  1816. Nerr = RegQueryValueExW(hKey,
  1817. PROVIDER_NAME_VALUE,
  1818. 0L,
  1819. &datatype,
  1820. (LPBYTE)buffer,
  1821. &buffersize) ;
  1822. }
  1823. RegCloseKey(hKey);
  1824. if (Nerr != ERROR_SUCCESS)
  1825. {
  1826. FreeMem(buffer);
  1827. return(NULL) ; // treat as cannot read
  1828. }
  1829. return ((WCHAR *) buffer);
  1830. }
  1831. /***
  1832. * MapWildCard()
  1833. * Maps the wildcard ASTERISK to next avail drive.
  1834. *
  1835. * Args:
  1836. * dev - the input string. Must NOT be NULL.
  1837. *
  1838. * Returns:
  1839. * dev unchanged if it is not the wildcard
  1840. * pointer to next avail drive id dev is wildcard
  1841. * NULL if there are no avail drives left
  1842. */
  1843. LPTSTR
  1844. MapWildCard(
  1845. LPTSTR dev,
  1846. LPTSTR startdev
  1847. )
  1848. {
  1849. static TCHAR new_dev[DEVLEN+1] ;
  1850. //
  1851. // if not the wold card char, just return it unchanged
  1852. //
  1853. if (!IsWildCard(dev))
  1854. {
  1855. return dev ;
  1856. }
  1857. //
  1858. // need find the next avail drive letter
  1859. // note: the char advance does not need to be DBCS safe,
  1860. // since we are only dealing with drive letters.
  1861. //
  1862. if ( startdev != NULL )
  1863. {
  1864. _tcscpy(new_dev, startdev);
  1865. }
  1866. else
  1867. {
  1868. _tcscpy(new_dev,TEXT("Z:\\")) ;
  1869. }
  1870. while ( TRUE )
  1871. {
  1872. if (GetDriveType(new_dev) == 1) // 1 means root not found
  1873. {
  1874. //
  1875. // check if it's a remembered connection
  1876. //
  1877. DWORD status;
  1878. TCHAR remote_name[40]; // length doesn't matter since we
  1879. // check for WN_MORE_DATA
  1880. DWORD length = sizeof(remote_name)/sizeof(TCHAR);
  1881. new_dev[2] = 0 ;
  1882. status = WNetGetConnection(new_dev, remote_name, &length);
  1883. if (status == WN_CONNECTION_CLOSED ||
  1884. status == WN_MORE_DATA ||
  1885. status == WN_SUCCESS)
  1886. {
  1887. //
  1888. // it's a remembered connection; try the next drive
  1889. //
  1890. new_dev[2] = TEXT('\\');
  1891. }
  1892. else
  1893. {
  1894. return (new_dev) ;
  1895. }
  1896. }
  1897. if ( new_dev[0] == 'c' || new_dev[0] == 'C' )
  1898. {
  1899. break;
  1900. }
  1901. --new_dev[0] ;
  1902. }
  1903. //
  1904. // if we got here, there were no drives left
  1905. //
  1906. return NULL ;
  1907. }