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.

933 lines
26 KiB

  1. // Copyright (c) 1998-1999 Microsoft Corporation
  2. /******************************************************************************
  3. *
  4. * MSG.C
  5. * Send a message to another user.
  6. *
  7. * Syntax:
  8. *
  9. * MSG [username] [/TIME:seconds] [/V] [/W] [/?] [message]\n" \
  10. * MSG [WinStationName] [/TIME:seconds] [/V] [/W] [/?] [message]\n" \
  11. * MSG [logonid] [/TIME:seconds] [/V] [/W] [/?] [message]\n" \
  12. * MSG [@filename] [/TIME:seconds] [/V] [/W] [/?] [message]\n" \
  13. *
  14. * /TIME:seconds - time delay to wait for receiver to acknowledge msg\n" \
  15. * /V - display information about actions being performed\n" \
  16. * /W - wait for response from user, useful with /V
  17. * /? - display syntax and options\n"
  18. *
  19. * Parameters:
  20. *
  21. * username
  22. * Identifies all logins belonging to the specific username
  23. *
  24. * winstationname
  25. * Identifies all logins connected to the winstation name regardless
  26. * of loginname.
  27. *
  28. * logonid
  29. * Decimal number specifying a logon id to send the message to
  30. *
  31. * @filename
  32. * Identifies a file that contains usernames or winstation names to
  33. * send messages to.
  34. *
  35. * Options:
  36. *
  37. * /SELF >>>> UNPUBLISHED <<<<
  38. * Send message to caller of msg. Used to send a message to
  39. * users when maintenace mode is enabled.
  40. *
  41. * /TIME:seconds (time delay)
  42. * The amount of time to wait for an acknowledgement from the target
  43. * login that the message has been received.
  44. *
  45. * /V (verbose)
  46. * Display information about the actions being performed.
  47. *
  48. * /? (help)
  49. * Display the syntax for the utility and information about the
  50. * options.
  51. *
  52. * message
  53. * The text of the message to send. If the text is not specified
  54. * then the text is read from STDIN.
  55. *
  56. * Remarks:
  57. *
  58. * The message can be typed on the command line or be read from STDIN.
  59. * The message is sent via a popup. The user receiving the popup can
  60. * hit a key to get rid of it or it will go away after a default timeout.
  61. *
  62. * If the target of the message is a terminal, then the message is
  63. * sent to all logins on the target terminal.
  64. *
  65. *
  66. *******************************************************************************/
  67. #include <nt.h>
  68. #include <ntrtl.h>
  69. #include <nturtl.h>
  70. #include <stdio.h>
  71. #include <windows.h>
  72. #include <ntddkbd.h>
  73. #include <ntddmou.h>
  74. #include <winstaw.h>
  75. #include <stdlib.h>
  76. #include <time.h>
  77. #include <utilsub.h>
  78. #include <process.h>
  79. #include <string.h>
  80. #include <malloc.h>
  81. #include <wchar.h>
  82. #include <io.h> // for isatty
  83. #include <locale.h>
  84. #include <winnlsp.h>
  85. #include "msg.h"
  86. #include "printfoa.h"
  87. // max length of the locale string
  88. #define MAX_LOCALE_STRING 64
  89. /*=============================================================================
  90. == Global Data
  91. =============================================================================*/
  92. ULONG Seconds;
  93. USHORT file_flag=FALSE; //wkp
  94. USHORT v_flag;
  95. USHORT self_flag;
  96. USHORT help_flag;
  97. WCHAR ids_input[MAX_IDS_LEN];
  98. PWCHAR MsgText, MsgTitle;
  99. WCHAR MsgLine[MAX_IDS_LEN];
  100. ULONG gCurrentLogonId = (ULONG)(-1);
  101. BOOLEAN wait_flag = FALSE;
  102. HANDLE hServerName = SERVERNAME_CURRENT;
  103. WCHAR ServerName[MAX_IDS_LEN+1];
  104. /*
  105. * The token map structure is used for parsing program arguments
  106. */
  107. TOKMAP ptm[] = {
  108. { TOKEN_INPUT, TMFLAG_REQUIRED, TMFORM_STRING, MAX_IDS_LEN,
  109. ids_input },
  110. { TOKEN_SERVER, TMFLAG_OPTIONAL, TMFORM_STRING, MAX_IDS_LEN,
  111. ServerName},
  112. { TOKEN_MESSAGE, TMFLAG_OPTIONAL, TMFORM_X_STRING, MAX_IDS_LEN,
  113. MsgLine },
  114. { TOKEN_TIME, TMFLAG_OPTIONAL, TMFORM_ULONG, sizeof(ULONG),
  115. &Seconds },
  116. { TOKEN_VERBOSE, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT),
  117. &v_flag },
  118. { TOKEN_WAIT, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(BOOLEAN),
  119. &wait_flag },
  120. { TOKEN_SELF, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT),
  121. &self_flag },
  122. { TOKEN_HELP, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT),
  123. &help_flag },
  124. { 0, 0, 0, 0, 0}
  125. };
  126. /*
  127. * This is the list of names we are to send the message to
  128. */
  129. int NameListCount = 0;
  130. WCHAR **NameList = NULL;
  131. WCHAR CurrUserName[USERNAME_LENGTH];
  132. /*
  133. * Local function prototypes
  134. */
  135. BOOLEAN SendMessageIfTarget( PLOGONID Id, ULONG Count,
  136. LPWSTR pTitle, LPWSTR pMessage );
  137. BOOLEAN CheckMatchList( PLOGONID );
  138. BOOLEAN MessageSend( PLOGONID pLId, LPWSTR pTitle, LPWSTR pMessage );
  139. BOOLEAN LoadFileToNameList( PWCHAR pName );
  140. BOOL ReadFileByLine( HANDLE, PCHAR, DWORD, PDWORD );
  141. void Usage( BOOLEAN bError );
  142. /*****************************************************************************
  143. *
  144. * MAIN
  145. *
  146. * ENTRY:
  147. * argc - count of the command line arguments.
  148. * argv - vector of strings containing the command line arguments.
  149. *
  150. ****************************************************************************/
  151. int __cdecl
  152. main(INT argc, CHAR **argv)
  153. {
  154. // struct tm * pTimeDate;
  155. // time_t curtime;
  156. SYSTEMTIME st;
  157. WCHAR TimeStamp[ MAX_TIME_DATE_LEN ];
  158. WCHAR *CmdLine;
  159. WCHAR **argvW;
  160. WCHAR szTitleFormat[50];
  161. DWORD dwSize;
  162. PLOGONID pTerm;
  163. UINT TermCount;
  164. ULONG Status;
  165. int i, rc, TitleLen;
  166. BOOLEAN MatchedOne = FALSE;
  167. VOID* pBuf;
  168. WCHAR wszString[MAX_LOCALE_STRING + 1];
  169. setlocale(LC_ALL, ".OCP");
  170. // We don't want LC_CTYPE set the same as the others or else we will see
  171. // garbage output in the localized version, so we need to explicitly
  172. // set it to correct console output code page
  173. _snwprintf(wszString, sizeof(wszString)/sizeof(WCHAR), L".%d", GetConsoleOutputCP());
  174. wszString[sizeof(wszString)/sizeof(WCHAR) - 1] = L'\0';
  175. _wsetlocale(LC_CTYPE, wszString);
  176. SetThreadUILanguage(0);
  177. /*
  178. * Massage the command line.
  179. */
  180. argvW = MassageCommandLine((DWORD)argc);
  181. if (argvW == NULL) {
  182. ErrorPrintf(IDS_ERROR_MALLOC);
  183. return(FAILURE);
  184. }
  185. /*
  186. * parse the cmd line without parsing the program name (argc-1, argv+1)
  187. */
  188. rc = ParseCommandLine(argc-1, argvW+1, ptm, 0);
  189. /*
  190. * Check for error from ParseCommandLine
  191. */
  192. if (rc && (rc & PARSE_FLAG_NO_PARMS) )
  193. help_flag = TRUE;
  194. if ( help_flag || rc ) {
  195. if (!help_flag) {
  196. Usage(TRUE);
  197. return(FAILURE);
  198. } else {
  199. Usage(FALSE);
  200. return(SUCCESS);
  201. }
  202. }
  203. // If no remote server was specified, then check if we are running under Terminal Server
  204. if ((!IsTokenPresent(ptm, TOKEN_SERVER) ) && (!AreWeRunningTerminalServices()))
  205. {
  206. ErrorPrintf(IDS_ERROR_NOT_TS);
  207. return(FAILURE);
  208. }
  209. /*
  210. * Open the specified server
  211. */
  212. if( ServerName[0] ) {
  213. hServerName = WinStationOpenServer( ServerName );
  214. if( hServerName == NULL ) {
  215. StringErrorPrintf(IDS_ERROR_SERVER,ServerName);
  216. PutStdErr( GetLastError(), 0 );
  217. return(FAILURE);
  218. }
  219. }
  220. /*
  221. * if no timeout was specified, use default
  222. */
  223. if ( !IsTokenPresent(ptm, TOKEN_TIME) )
  224. Seconds = RESPONSE_TIMEOUT;
  225. /*
  226. * allocate a buffer for the message header
  227. */
  228. if ( (MsgText = (PWCHAR)malloc(MAX_IDS_LEN * sizeof(WCHAR))) == NULL ) {
  229. ErrorPrintf(IDS_ERROR_MALLOC);
  230. return(FAILURE);
  231. }
  232. MsgText[0] = 0;
  233. /*
  234. * set up message header text: sender and timestamp
  235. */
  236. GetCurrentUserName(CurrUserName, USERNAME_LENGTH);
  237. /*
  238. * Get the current Winstation Id for this process
  239. */
  240. gCurrentLogonId = GetCurrentLogonId();
  241. /*
  242. * Form message title string.
  243. */
  244. dwSize = sizeof(szTitleFormat) / sizeof(WCHAR);
  245. LoadString(NULL,IDS_TITLE_FORMAT,szTitleFormat,dwSize);
  246. TitleLen = (wcslen(szTitleFormat) + wcslen(CurrUserName) + 1) * sizeof(WCHAR) + ( 2 * sizeof( TimeStamp ) );
  247. MsgTitle = (PWCHAR)malloc(TitleLen);
  248. if( MsgTitle == NULL )
  249. {
  250. ErrorPrintf(IDS_ERROR_MALLOC);
  251. return(FAILURE);
  252. }
  253. _snwprintf(MsgTitle, TitleLen, szTitleFormat, CurrUserName);
  254. TimeStamp[0] = 0;
  255. GetLocalTime( &st );
  256. GetDateFormat( LOCALE_USER_DEFAULT ,
  257. DATE_SHORTDATE ,
  258. &st ,
  259. NULL ,
  260. TimeStamp,
  261. MAX_TIME_DATE_LEN );
  262. wcscat(MsgTitle , TimeStamp);
  263. TimeStamp[0] = 0;
  264. GetTimeFormat( LOCALE_USER_DEFAULT ,
  265. TIME_NOSECONDS ,
  266. &st ,
  267. NULL ,
  268. TimeStamp,
  269. MAX_TIME_DATE_LEN );
  270. wcscat(MsgTitle , L" " );
  271. wcscat(MsgTitle , TimeStamp);
  272. /*
  273. * if message was specified on the command line, add it to MsgText string
  274. */
  275. if ( IsTokenPresent(ptm, TOKEN_MESSAGE) ) {
  276. pBuf = realloc(MsgText, (wcslen(MsgText) + wcslen(MsgLine) + 1) * sizeof(WCHAR));
  277. if (pBuf) {
  278. MsgText = pBuf;
  279. }
  280. else {
  281. ErrorPrintf(IDS_ERROR_MALLOC);
  282. free(MsgText);
  283. return(FAILURE);
  284. }
  285. wcscat(MsgText, MsgLine);
  286. } else {
  287. /*
  288. * Message was not on the command line. If STDIN is connected to
  289. * the keyboard, then prompt the user for the message to send,
  290. * otherwise just read STDIN.
  291. */
  292. if ( _isatty( _fileno(stdin) ) )
  293. Message(IDS_MESSAGE_PROMPT);
  294. while ( wfgets(MsgLine, MAX_IDS_LEN, stdin) != NULL ) {
  295. pBuf = (PWCHAR)realloc(
  296. MsgText,
  297. (wcslen(MsgText) + wcslen(MsgLine) + 1) * sizeof(WCHAR) );
  298. if (pBuf) {
  299. MsgText = pBuf;
  300. }
  301. else {
  302. ErrorPrintf(IDS_ERROR_MALLOC);
  303. free(MsgText);
  304. return(FAILURE);
  305. }
  306. wcscat(MsgText, MsgLine);
  307. }
  308. /*
  309. * When we fall through, we either have an eof or a problem with
  310. * STDIN
  311. */
  312. if ( feof(stdin) ) {
  313. /*
  314. * If we get here then we hit eof on STDIN. First check to make
  315. * sure that we did not get an eof on first wfgets
  316. */
  317. if ( !wcslen(MsgText) ) {
  318. ErrorPrintf(IDS_ERROR_EMPTY_MESSAGE);
  319. return(FAILURE);
  320. }
  321. } else {
  322. /*
  323. * The return from wfgets was not eof so we have an STDIN
  324. * problem
  325. */
  326. ErrorPrintf(IDS_ERROR_STDIN_PROCESSING);
  327. return(FAILURE);
  328. }
  329. }
  330. /*
  331. * Is the ids_input really a file indirection?
  332. */
  333. if ( ids_input[0] == L'@' ) {
  334. /*
  335. * Open the input file and read the names into the NameList
  336. */
  337. if ( !LoadFileToNameList(&ids_input[1]) )
  338. return(FAILURE);
  339. /*
  340. * Ok, let's get in touch
  341. */
  342. file_flag = TRUE;
  343. } else {
  344. _wcslwr( ids_input );
  345. NameList = (WCHAR **)malloc( 2 * sizeof( WCHAR * ) );
  346. if ( NameList == NULL ) {
  347. ErrorPrintf(IDS_ERROR_MALLOC);
  348. return(FAILURE);
  349. }
  350. NameList[0] = ids_input;
  351. NameList[1] = NULL;
  352. NameListCount = 1;
  353. }
  354. /*
  355. * Enumerate across all the WinStations and send the message
  356. * to them if there are any matches in the NameList
  357. */
  358. if ( WinStationEnumerate(hServerName, &pTerm, &TermCount) ) {
  359. if ( SendMessageIfTarget(pTerm, TermCount, MsgTitle, MsgText) )
  360. MatchedOne = TRUE;
  361. WinStationFreeMemory(pTerm);
  362. } else{
  363. Status = GetLastError();
  364. ErrorPrintf(IDS_ERROR_WINSTATION_ENUMERATE, Status);
  365. return(FAILURE);
  366. }
  367. /*
  368. * Check for at least one match
  369. */
  370. if ( !MatchedOne ) {
  371. if( file_flag )
  372. StringErrorPrintf(IDS_ERROR_NO_FILE_MATCHING, &ids_input[1]);
  373. else
  374. StringErrorPrintf(IDS_ERROR_NO_MATCHING, ids_input);
  375. return(FAILURE);
  376. }
  377. return(SUCCESS);
  378. } /* main() */
  379. /******************************************************************************
  380. *
  381. * SendMessageIfTarget - Send a Message to a group of WinStations if
  382. * their the target as specified by TargetName.
  383. *
  384. * ENTRY
  385. * LId (input)
  386. * Pointer to an array of LOGONIDs returned from WinStationEnumerate()
  387. * Count (input)
  388. * Number of elements in LOGONID array.
  389. * pTitle (input)
  390. * Points to message title string.
  391. * pMessage (input)
  392. * Points to message string.
  393. *
  394. * EXIT
  395. * TRUE if message was sent to at least one WinStation; FALSE otherwise.
  396. *
  397. *****************************************************************************/
  398. BOOLEAN
  399. SendMessageIfTarget( PLOGONID Id,
  400. ULONG Count,
  401. LPWSTR pTitle,
  402. LPWSTR pMessage )
  403. {
  404. ULONG i;
  405. BOOLEAN MatchedOne = FALSE;
  406. for ( i=0; i < Count ; i++ ) {
  407. /*
  408. * Look at Id->WinStationName, get its User, etc. and compare
  409. * against targetname(s). Accept '*' as "everything".
  410. */
  411. if( CheckMatchList( Id ) )
  412. {
  413. MatchedOne = TRUE;
  414. MessageSend(Id, pTitle, pMessage);
  415. }
  416. Id++;
  417. }
  418. return( MatchedOne );
  419. } /* SendMessageIfTarget() */
  420. /******************************************************************************
  421. *
  422. * CheckMatchList - Returns TRUE if the current WinStation is a match for
  423. * sending a message due to either its name, id, or the
  424. * name of its logged on user being in the message target(s)
  425. * list.
  426. *
  427. * ENTRY
  428. * LId (input)
  429. * Pointer to a LOGONID returned from WinStationEnumerate()
  430. *
  431. * EXIT
  432. * TRUE if this is a match, FALSE otherwise.
  433. *
  434. *****************************************************************************/
  435. BOOLEAN
  436. CheckMatchList( PLOGONID LId )
  437. {
  438. int i;
  439. /*
  440. * Wild card matches everything
  441. */
  442. if ( ids_input[0] == L'*' ) {
  443. return(TRUE);
  444. }
  445. /*
  446. * Loop through name list to see if any given name applies to
  447. * this WinStation
  448. */
  449. for( i=0; i<NameListCount; i++ ) {
  450. if (WinStationObjectMatch( hServerName , LId, NameList[i]) ) {
  451. return(TRUE);
  452. }
  453. }
  454. return(FALSE);
  455. }
  456. /******************************************************************************
  457. *
  458. * MessageSend - Send a message to the target WinStation
  459. *
  460. * ENTRY
  461. * LId (input)
  462. * Pointer to a LOGONID returned from WinStationEnumerate()
  463. * pTitle (input)
  464. * Points to message title string.
  465. * pMessage (input)
  466. * Points to message string.
  467. *
  468. * EXIT
  469. * TRUE message is sent, FALSE otherwise.
  470. *
  471. *****************************************************************************/
  472. BOOLEAN
  473. MessageSend( PLOGONID LId,
  474. LPWSTR pTitle,
  475. LPWSTR pMessage )
  476. {
  477. ULONG idResponse, ReturnLength;
  478. WINSTATIONINFORMATION WSInfo;
  479. /*
  480. * Make sure that the WinStation is in the 'connected' state
  481. */
  482. if ( !WinStationQueryInformation( hServerName,
  483. LId->LogonId,
  484. WinStationInformation,
  485. &WSInfo,
  486. sizeof(WSInfo),
  487. &ReturnLength ) ) {
  488. goto BadQuery;
  489. }
  490. if ( WSInfo.ConnectState != State_Connected &&
  491. WSInfo.ConnectState != State_Active ) {
  492. goto NotConnected;
  493. }
  494. /*
  495. * Send message.
  496. */
  497. if ( v_flag ) {
  498. if( LId->WinStationName[0] )
  499. StringDwordMessage(IDS_MESSAGE_WS, LId->WinStationName, Seconds);
  500. else
  501. Message(IDS_MESSAGE_ID, LId->LogonId, Seconds);
  502. }
  503. if ( !WinStationSendMessage( hServerName,
  504. LId->LogonId,
  505. pTitle,
  506. (wcslen(pTitle))*sizeof(WCHAR),
  507. pMessage,
  508. (wcslen(pMessage))*sizeof(WCHAR),
  509. MB_OK, // MessageBox() Style
  510. Seconds,
  511. &idResponse,
  512. (BOOLEAN)(!wait_flag) ) ) {
  513. if( LId->WinStationName[0] )
  514. StringDwordErrorPrintf(IDS_ERROR_MESSAGE_WS, LId->WinStationName, GetLastError() );
  515. else
  516. ErrorPrintf(IDS_ERROR_MESSAGE_ID, LId->LogonId, GetLastError() );
  517. PutStdErr(GetLastError(), 0);
  518. goto BadMessage;
  519. }
  520. /*
  521. * Output response result if verbose mode.
  522. */
  523. if( v_flag ) {
  524. switch( idResponse ) {
  525. case IDTIMEOUT:
  526. if( LId->WinStationName[0] )
  527. StringMessage(IDS_MESSAGE_RESPONSE_TIMEOUT_WS,
  528. LId->WinStationName);
  529. else
  530. Message(IDS_MESSAGE_RESPONSE_TIMEOUT_ID, LId->LogonId);
  531. break;
  532. case IDASYNC:
  533. if( LId->WinStationName[0] )
  534. StringMessage(IDS_MESSAGE_RESPONSE_ASYNC_WS,
  535. LId->WinStationName);
  536. else
  537. Message(IDS_MESSAGE_RESPONSE_ASYNC_ID, LId->LogonId);
  538. break;
  539. case IDCOUNTEXCEEDED:
  540. if( LId->WinStationName[0] )
  541. StringMessage(IDS_MESSAGE_RESPONSE_COUNT_EXCEEDED_WS,
  542. LId->WinStationName);
  543. else
  544. Message(IDS_MESSAGE_RESPONSE_COUNT_EXCEEDED_ID,
  545. LId->LogonId);
  546. break;
  547. case IDDESKTOPERROR:
  548. if( LId->WinStationName[0] )
  549. StringMessage(IDS_MESSAGE_RESPONSE_DESKTOP_ERROR_WS,
  550. LId->WinStationName);
  551. else
  552. Message(IDS_MESSAGE_RESPONSE_DESKTOP_ERROR_ID,
  553. LId->LogonId);
  554. break;
  555. case IDERROR:
  556. if( LId->WinStationName[0] )
  557. StringMessage(IDS_MESSAGE_RESPONSE_ERROR_WS,
  558. LId->WinStationName);
  559. else
  560. Message(IDS_MESSAGE_RESPONSE_ERROR_ID,
  561. LId->LogonId);
  562. break;
  563. case IDOK:
  564. case IDCANCEL:
  565. if( LId->WinStationName[0] )
  566. StringMessage(IDS_MESSAGE_RESPONSE_WS,
  567. LId->WinStationName);
  568. else
  569. Message(IDS_MESSAGE_RESPONSE_ID,
  570. LId->LogonId);
  571. break;
  572. default:
  573. if( LId->WinStationName[0] )
  574. DwordStringMessage(IDS_MESSAGE_RESPONSE_WS,
  575. idResponse, LId->WinStationName);
  576. else
  577. Message(IDS_MESSAGE_RESPONSE_ID,
  578. idResponse, LId->LogonId);
  579. break;
  580. }
  581. }
  582. return(TRUE);
  583. /*-------------------------------------
  584. * Error cleanup and return
  585. */
  586. BadMessage:
  587. NotConnected:
  588. BadQuery:
  589. return(FALSE);
  590. } /* MessageSend() */
  591. /******************************************************************************
  592. *
  593. * LoadFileToNameList
  594. *
  595. * Load names from a file into the input name list.
  596. *
  597. * ENTRY:
  598. * pName Name of the file to load from
  599. *
  600. * EXIT:
  601. * TRUE for sucessful name load from file; FALSE if error.
  602. *
  603. * An appropriate error message will have been displayed on error.
  604. *
  605. *****************************************************************************/
  606. BOOLEAN
  607. LoadFileToNameList( PWCHAR pName )
  608. {
  609. HANDLE hFile;
  610. INT CurrentSize;
  611. VOID* pBuf;
  612. /*
  613. * Open input file.
  614. */
  615. hFile = CreateFile(
  616. pName,
  617. GENERIC_READ,
  618. FILE_SHARE_READ | FILE_SHARE_WRITE,
  619. NULL,
  620. OPEN_EXISTING,
  621. FILE_ATTRIBUTE_NORMAL,
  622. NULL
  623. );
  624. if (hFile == INVALID_HANDLE_VALUE) {
  625. StringErrorPrintf(IDS_ERROR_CANT_OPEN_INPUT_FILE, pName);
  626. PutStdErr(GetLastError(), 0);
  627. return(FALSE);
  628. }
  629. /*
  630. * Allocate a large array for the name string pointers
  631. */
  632. CurrentSize = 100;
  633. if ( !(NameList = (WCHAR **)malloc(CurrentSize * sizeof(WCHAR *))) ) {
  634. ErrorPrintf(IDS_ERROR_MALLOC);
  635. return(FAILURE);
  636. }
  637. NameListCount = 0;
  638. while( 1 ) {
  639. BOOL fRet;
  640. CHAR *pBuffer;
  641. DWORD nBytesRead;
  642. WCHAR *pwBuffer;
  643. /*
  644. * See if we need to grow the list
  645. */
  646. if( NameListCount == CurrentSize ) {
  647. pBuf = realloc(NameList, CurrentSize+100);
  648. if (!pBuf) {
  649. ErrorPrintf(IDS_ERROR_MALLOC);
  650. free(NameList);
  651. return(FAILURE);
  652. }
  653. NameList = (WCHAR **)pBuf;
  654. CurrentSize += 100;
  655. }
  656. pBuffer = (CHAR *)LocalAlloc(LPTR, USERNAME_LENGTH * sizeof(CHAR));
  657. if (pBuffer == NULL) {
  658. ErrorPrintf(IDS_ERROR_MALLOC);
  659. return(FAILURE);
  660. }
  661. fRet = ReadFileByLine(
  662. hFile,
  663. pBuffer,
  664. USERNAME_LENGTH,
  665. &nBytesRead
  666. );
  667. if (fRet && (nBytesRead > 0)) {
  668. INT cWChar;
  669. cWChar = MultiByteToWideChar(
  670. CP_ACP,
  671. MB_PRECOMPOSED,
  672. pBuffer,
  673. -1,
  674. NULL,
  675. 0
  676. );
  677. pwBuffer = (WCHAR *)LocalAlloc(LPTR, (cWChar + 1) * sizeof(WCHAR));
  678. if (pwBuffer != NULL) {
  679. MultiByteToWideChar(
  680. CP_ACP,
  681. MB_PRECOMPOSED,
  682. pBuffer,
  683. -1,
  684. pwBuffer,
  685. cWChar
  686. );
  687. } else {
  688. ErrorPrintf(IDS_ERROR_MALLOC);
  689. return(FAILURE);
  690. }
  691. if (pwBuffer[wcslen(pwBuffer)-1] == L'\n') {
  692. pwBuffer[wcslen(pwBuffer)-1] = (WCHAR)NULL;
  693. }
  694. _wcslwr(pwBuffer);
  695. NameList[NameListCount++] = pwBuffer;
  696. } else {
  697. NameList[NameListCount] = NULL;
  698. CloseHandle(hFile);
  699. return(TRUE);
  700. }
  701. }
  702. } /* LoadFileToNameList() */
  703. BOOL
  704. ReadFileByLine(
  705. HANDLE hFile,
  706. PCHAR pBuffer,
  707. DWORD cbBuffer,
  708. PDWORD pcbBytesRead
  709. )
  710. {
  711. BOOL fRet;
  712. fRet = ReadFile(
  713. hFile,
  714. pBuffer,
  715. cbBuffer - 1,
  716. pcbBytesRead,
  717. NULL
  718. );
  719. if (fRet && (*pcbBytesRead > 0)) {
  720. CHAR* pNewLine;
  721. pNewLine = strstr(pBuffer, "\r\n");
  722. if (pNewLine != NULL) {
  723. LONG lOffset;
  724. lOffset = (LONG)(pNewLine + 2 - pBuffer) - (*pcbBytesRead);
  725. if (SetFilePointer(hFile, lOffset, NULL, FILE_CURRENT) ==
  726. 0xFFFFFFFF) {
  727. return(FALSE);
  728. }
  729. *pNewLine = (CHAR)NULL;
  730. }
  731. }
  732. return(fRet);
  733. }
  734. /*******************************************************************************
  735. *
  736. * Usage
  737. *
  738. * Output the usage message for this utility.
  739. *
  740. * ENTRY:
  741. * bError (input)
  742. * TRUE if the 'invalid parameter(s)' message should preceed the usage
  743. * message and the output go to stderr; FALSE for no such error
  744. * string and output goes to stdout.
  745. *
  746. * EXIT:
  747. *
  748. *
  749. ******************************************************************************/
  750. void
  751. Usage( BOOLEAN bError )
  752. {
  753. if ( bError ) {
  754. ErrorPrintf(IDS_ERROR_INVALID_PARAMETERS);
  755. ErrorPrintf(IDS_USAGE1);
  756. ErrorPrintf(IDS_USAGE2);
  757. ErrorPrintf(IDS_USAGE3);
  758. ErrorPrintf(IDS_USAGE4);
  759. ErrorPrintf(IDS_USAGE5);
  760. ErrorPrintf(IDS_USAGE6);
  761. ErrorPrintf(IDS_USAGE7);
  762. ErrorPrintf(IDS_USAGE8);
  763. ErrorPrintf(IDS_USAGE9);
  764. ErrorPrintf(IDS_USAGEA);
  765. ErrorPrintf(IDS_USAGEB);
  766. ErrorPrintf(IDS_USAGEC);
  767. ErrorPrintf(IDS_USAGED);
  768. ErrorPrintf(IDS_USAGEE);
  769. ErrorPrintf(IDS_USAGEF);
  770. }
  771. else
  772. {
  773. Message(IDS_USAGE1);
  774. Message(IDS_USAGE2);
  775. Message(IDS_USAGE3);
  776. Message(IDS_USAGE4);
  777. Message(IDS_USAGE5);
  778. Message(IDS_USAGE6);
  779. Message(IDS_USAGE7);
  780. Message(IDS_USAGE8);
  781. Message(IDS_USAGE9);
  782. Message(IDS_USAGEA);
  783. Message(IDS_USAGEB);
  784. Message(IDS_USAGEC);
  785. Message(IDS_USAGED);
  786. Message(IDS_USAGEE);
  787. Message(IDS_USAGEF);
  788. }
  789. } /* Usage() */