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.

1831 lines
50 KiB

  1. /*++
  2. Copyright (c) 1997-1998 Microsoft Corporation
  3. Module Name:
  4. client.c
  5. Abstract:
  6. This module contains the code to process OS Chooser message
  7. for the BINL server.
  8. Author:
  9. Adam Barr (adamba) 9-Jul-1997
  10. Geoff Pease (gpease) 10-Nov-1997
  11. Environment:
  12. User Mode - Win32
  13. Revision History:
  14. --*/
  15. #include "binl.h"
  16. #pragma hdrstop
  17. #include "mbstring.h"
  18. //
  19. // List of maximums for certain variables. OscAddVariableX will fail if
  20. // the limits are exceeded; it is up to the caller of the function to know
  21. // if the variable being added might hit a limit and check for failure.
  22. //
  23. typedef struct OSC_VARIABLE_MAXIMUM {
  24. LPSTR VariableName;
  25. ULONG MaximumLength;
  26. } OSC_VARIABLE_MAXIMUM, *POSC_VARIABLE_MAXIMUM;
  27. OSC_VARIABLE_MAXIMUM OscMaximums[] = {
  28. //
  29. // This set of variables come from locations we don't completely control,
  30. // so we need to check the return code from OscAddVariable each time.
  31. //
  32. { "BOOTFILE", 127 }, // with NULL, must fit in 128-byte field of CREATE_DATA.
  33. // Normally this will be empty or come from a .sif;
  34. // an admin may customize the .sif or modify the
  35. // DS attribute directly.
  36. { "MACHINENAME", 63 }, // used in path with SERVERNAME; comes from a screen
  37. // input with a max length of 63, or else is generated
  38. // by the GenerateMachineName() function. 63 is equal
  39. // to DNS_MAX_LABEL_LENGTH.
  40. { "SIFFILE", 127 }, // with NULL, must fit in 128-byte field of CREATE_DATA.
  41. // Normally this will be \RemoteInstall\tmp\[GUID].sif,
  42. // but the path may be longer.
  43. { "INSTALLPATH", 127 }, // used in paths with MACHINETYPE and SERVERNAME. This
  44. // will depend on where the build is installed
  45. // with RISETUP.
  46. //
  47. // The ones after this will be correct when we add them, but a rogue
  48. // client might send in bogus values. So the general checking code in
  49. // OscProcessScreenArguments will catch invalid ones.
  50. //
  51. { "MACHINETYPE", MAX_ARCHITECTURE_LENGTH },
  52. // current max value. This is sent up by oschooser
  53. // and should correspond to where RISETUP puts
  54. // the platform-specific files.
  55. { "SERVERNAME", 63 }, // used in paths with MACHINENAME and INSTALLPATH,
  56. // set by calling GetComputerNameEX(ComputerNameNetBIOS)
  57. { "NETBIOSNAME", 31 }, // with NULL, must fit in 32-byte field of CREATE_DATA.
  58. // This is gotten by calling DnsHostnameToComputerNameW(),
  59. // if that fails the name is truncated to 15 chars
  60. { "LANGUAGE", 32 }, // reasonable max value; this is obtained by calling
  61. // GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_SENGLANGUAGE),
  62. // but can be over-ridden in the registry. It is used in
  63. // paths with IntelliMirrorPathW and some other constants,
  64. // but no other variables. Eventually this becomes
  65. // a part of INSTALLPATH and sometimes BOOTFILE.
  66. { "GUID", 32 }, // 16 bytes in hex format
  67. { "MAC", 12 }, // 6 bytes in hex format
  68. //
  69. // NOTE: If we get an error condition, we add the variable SUBERROR
  70. // to the client state. So don't put a limit on SUBERROR size, since
  71. // that might cause an infinite loop.
  72. //
  73. };
  74. #define OSC_VARIABLE_MAXIMUM_COUNT (sizeof(OscMaximums) / sizeof(OSC_VARIABLE_MAXIMUM))
  75. //
  76. // We need to eliminate the chance of denial of service attacks so we'll limit
  77. // the number of concurrent clients we support.
  78. //
  79. #define BINL_MAX_CLIENT_RECORDS 1000
  80. LONG BinlGlobalClientLimit = BINL_MAX_CLIENT_RECORDS;
  81. DWORD
  82. OscUpdatePassword(
  83. IN PCLIENT_STATE ClientState,
  84. IN PWCHAR SamAccountName,
  85. IN PWCHAR Password,
  86. IN LDAP * LdapHandle,
  87. IN PLDAPMessage LdapMessage
  88. )
  89. /*++
  90. Routine Description:
  91. Sets the password for the client. NOTE: WE MUST BE BETWEEN CALLS TO
  92. OSCIMPERSONATE/OSCREVERT.
  93. Arguments:
  94. ClientState - The client state. AuthenticatedDCLdapHandle must be valid
  95. and we must be impersonating the client.
  96. SamAccountName - The name of the machine account. This is the
  97. "samAccountName" value from the DS, which includes the final $.
  98. Password - The NULL-terminated Unicode password.
  99. LdapHandle - The handle to the DS.
  100. LdapMessage - The result of an ldap search for this client.
  101. Return Value:
  102. Status of the operation.
  103. --*/
  104. {
  105. BOOL bResult;
  106. LDAP * serverLdap;
  107. PWCHAR serverHostName;
  108. USER_INFO_1003 userInfo1003;
  109. PWCHAR backslashServerName;
  110. PWCHAR p;
  111. ULONG serverHostNameLength;
  112. DWORD paramError;
  113. NET_API_STATUS netStatus;
  114. //
  115. // Change the password in the DS.
  116. //
  117. serverLdap = ldap_conn_from_msg (LdapHandle, LdapMessage);
  118. if (serverLdap == NULL) {
  119. BinlPrintDbg(( DEBUG_ERRORS,
  120. "OscUpdatePassword ldap_conn_from_msg is NULL\n" ));
  121. return E_HANDLE;
  122. }
  123. serverHostName = NULL;
  124. if (LDAP_SUCCESS != ldap_get_option(serverLdap, LDAP_OPT_HOST_NAME, &serverHostName)) {
  125. BinlPrintDbg(( DEBUG_ERRORS,
  126. "OscUpdatePassword ldap_get_option failed\n" ));
  127. return E_HANDLE;
  128. }
  129. userInfo1003.usri1003_password = Password;
  130. serverHostNameLength = wcslen(serverHostName) + 1;
  131. //
  132. // Allocate room for the name with two extra characters
  133. // for the leading \\.
  134. //
  135. backslashServerName = BinlAllocateMemory((serverHostNameLength+2) * sizeof(WCHAR));
  136. if (backslashServerName == NULL) {
  137. BinlPrintDbg(( DEBUG_ERRORS,
  138. "OscUpdatePassword could not allocate serverHostNameW\n" ));
  139. return ERROR_NOT_ENOUGH_SERVER_MEMORY;
  140. }
  141. wcscpy(backslashServerName, L"\\\\");
  142. wcscpy(backslashServerName+2, serverHostName);
  143. //
  144. // TEMP: Serialize all calls to the NetUserSetInfo/
  145. // NetUserModalsGet. See discussion in bug 319962.
  146. // This code was put back in when the fix for a
  147. // problem described as "RPC is ignoring the security
  148. // context of the caller when choosing which named
  149. // pipe to send an RPC call over" turned out to cause
  150. // a BVT break.
  151. //
  152. EnterCriticalSection(&HackWorkaroundCriticalSection);
  153. netStatus = NetUserSetInfo(
  154. backslashServerName,
  155. SamAccountName,
  156. 1003,
  157. (LPBYTE)&userInfo1003,
  158. &paramError);
  159. LeaveCriticalSection(&HackWorkaroundCriticalSection);
  160. BinlFreeMemory(backslashServerName);
  161. if (netStatus != NERR_Success) {
  162. HANDLE TempToken;
  163. BinlPrint(( DEBUG_ERRORS,
  164. "OscUpdatePassword NetUserSetInfo returned %lx\n", netStatus ));
  165. //
  166. // If NetUserSetInfo failed, try a LogonUser to see if the
  167. // password is already set to the value we want -- if so,
  168. // we can still succeed.
  169. //
  170. bResult = LogonUser(
  171. SamAccountName,
  172. OscFindVariableW( ClientState, "MACHINEDOMAIN" ),
  173. Password,
  174. LOGON32_LOGON_NETWORK,
  175. LOGON32_PROVIDER_WINNT40,
  176. &TempToken);
  177. if (bResult) {
  178. CloseHandle(TempToken);
  179. } else {
  180. DWORD TempError = GetLastError();
  181. if (TempError != ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT) {
  182. return netStatus; // return the original error
  183. }
  184. }
  185. //
  186. // Fall through and return ERROR_SUCCESS.
  187. //
  188. }
  189. return ERROR_SUCCESS;
  190. }
  191. //
  192. // Free client state information
  193. //
  194. VOID
  195. FreeClient(
  196. PCLIENT_STATE client
  197. )
  198. {
  199. ULONG i;
  200. TraceFunc("FreeClient( )\n");
  201. BinlPrintDbg(( DEBUG_OSC, "Freeing client state for %s\n", inet_ntoa(*(struct in_addr *)&(client->RemoteIp)) ));
  202. DeleteCriticalSection(&client->CriticalSection);
  203. if (client->LastResponse)
  204. BinlFreeMemory(client->LastResponse);
  205. OscFreeClientVariables(client);
  206. InterlockedIncrement( &BinlGlobalClientLimit );
  207. if (client->NegotiateProcessed) {
  208. DeleteSecurityContext( &client->ServerContextHandle );
  209. }
  210. if (client->AuthenticatedDCLdapHandle) {
  211. ldap_unbind(client->AuthenticatedDCLdapHandle);
  212. }
  213. if (client->UserToken) {
  214. CloseHandle(client->UserToken);
  215. }
  216. BinlFreeMemory(client);
  217. }
  218. VOID
  219. OscFreeClientVariables(
  220. PCLIENT_STATE clientState
  221. )
  222. /*++
  223. Routine Description:
  224. This function frees all variables in a client state.
  225. Arguments:
  226. clientState - the client state pointer.
  227. Return Value:
  228. None.
  229. --*/
  230. {
  231. ULONG i;
  232. for( i = 0; i < clientState->nVariables; i++ )
  233. {
  234. BinlFreeMemory(clientState->Variables[i].pszToken);
  235. if (clientState->Variables[i].pszStringA) {
  236. BinlFreeMemory(clientState->Variables[i].pszStringA);
  237. clientState->Variables[i].pszStringA = NULL;
  238. }
  239. if (clientState->Variables[i].pszStringW) {
  240. BinlFreeMemory(clientState->Variables[i].pszStringW);
  241. clientState->Variables[i].pszStringW = NULL;
  242. }
  243. }
  244. clientState->nVariables = 0;
  245. clientState->fHaveSetupMachineDN = FALSE;
  246. clientState->fCreateNewAccount = FALSE;
  247. clientState->fAutomaticMachineName = FALSE;
  248. }
  249. BOOLEAN
  250. OscInitializeClientVariables(
  251. PCLIENT_STATE clientState
  252. )
  253. /*++
  254. Routine Description:
  255. This function cleans out any variables in the client state,
  256. then initializes some default values, which may later be
  257. overwritten by variables from client screens.
  258. This function is called when a client state is created, and
  259. when it is re-used from cache.
  260. Arguments:
  261. clientState - the client state pointer.
  262. Return Value:
  263. None.
  264. --*/
  265. {
  266. BOOLEAN retVal = TRUE;
  267. //
  268. // First clean out any variables in the client state.
  269. //
  270. OscFreeClientVariables(clientState);
  271. //
  272. // Now add the variables.
  273. //
  274. EnterCriticalSection( &gcsParameters );
  275. if (BinlGlobalDefaultLanguage) {
  276. OscAddVariableW( clientState, "LANGUAGE", BinlGlobalDefaultLanguage );
  277. } else {
  278. OscAddVariableW( clientState, "LANGUAGE", DEFAULT_LANGUAGE );
  279. }
  280. if (BinlGlobalDefaultOrgname) {
  281. OscAddVariableW( clientState, "ORGNAME", BinlGlobalDefaultOrgname );
  282. } else {
  283. OscAddVariableW( clientState, "ORGNAME", DEFAULT_ORGNAME );
  284. }
  285. if (BinlGlobalDefaultTimezone) {
  286. OscAddVariableW( clientState, "TIMEZONE", BinlGlobalDefaultTimezone );
  287. } else {
  288. OscAddVariableW( clientState, "TIMEZONE", DEFAULT_TIMEZONE );
  289. }
  290. if (BinlGlobalOurDomainName == NULL || BinlGlobalOurServerName == NULL) {
  291. LeaveCriticalSection( &gcsParameters );
  292. BinlPrintDbg((DEBUG_OSC_ERROR, "!! Error we don't have a FQDN for ourselves.\n" ));
  293. retVal = FALSE;
  294. goto Cleanup;
  295. }
  296. OscAddVariableW( clientState, "SERVERDOMAIN", BinlGlobalOurDomainName );
  297. // Add the Server's name variable
  298. OscAddVariableW( clientState, "SERVERNAME", BinlGlobalOurServerName );
  299. LeaveCriticalSection( &gcsParameters );
  300. OscAddVariableA( clientState, "SUBERROR", " " );
  301. clientState->fHaveSetupMachineDN = FALSE;
  302. clientState->fCreateNewAccount = FALSE;
  303. clientState->fAutomaticMachineName = FALSE;
  304. clientState->InitializeOnFirstRequest = FALSE;
  305. Cleanup:
  306. //
  307. // If this fails, clean up anything we set here.
  308. //
  309. if (!retVal) {
  310. OscFreeClientVariables(clientState);
  311. }
  312. return retVal;
  313. }
  314. DWORD
  315. OscFindClient(
  316. ULONG RemoteIp,
  317. BOOL Remove,
  318. PCLIENT_STATE * pClientState
  319. )
  320. /*++
  321. Routine Description:
  322. This function looks up a client in our client database, using
  323. their IP address. If Remove is TRUE, it removes the entry if
  324. found. Otherwise, if not found, it creates a new entry.
  325. Arguments:
  326. RemoteIp - the remote IP address.
  327. Remove - TRUE if the client should be removed if found.
  328. pClientState - Returns the CLIENT_STATE.
  329. Return Value:
  330. ERROR_SUCCESS if it finds the client and it is not in use.
  331. ERROR_NOT_ENOUGH_SERVER_MEMORY if a client state could not be allocated.
  332. ERROR_BUSY if the client state is already being used by another thread.
  333. --*/
  334. {
  335. LONG oldCount;
  336. PLIST_ENTRY p;
  337. DWORD Error = ERROR_SUCCESS;
  338. PCLIENT_STATE TempClient = NULL;
  339. TraceFunc("OscFindClient( )\n");
  340. EnterCriticalSection(&ClientsCriticalSection);
  341. for (p = ClientsQueue.Flink;
  342. p != &ClientsQueue;
  343. p = p->Flink) {
  344. TempClient = CONTAINING_RECORD(p, CLIENT_STATE, Linkage);
  345. if (TempClient->RemoteIp == RemoteIp) {
  346. //
  347. // Found it!
  348. //
  349. if (Remove) {
  350. RemoveEntryList(&TempClient->Linkage);
  351. TraceFunc("Client removed.\n");
  352. }
  353. break;
  354. }
  355. }
  356. if (p == &ClientsQueue) {
  357. TempClient = NULL;
  358. }
  359. if (!TempClient && (!Remove)) {
  360. //
  361. // Not found, allocate a new one.
  362. //
  363. oldCount = InterlockedDecrement( &BinlGlobalClientLimit );
  364. if (oldCount <= 0) {
  365. InterlockedIncrement( &BinlGlobalClientLimit );
  366. BinlPrintDbg(( DEBUG_OSC_ERROR, "Way too many clients, 0x%x clients\n", BINL_MAX_CLIENT_RECORDS));
  367. Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
  368. TempClient = NULL;
  369. } else {
  370. TraceFunc("Creating new client...\n");
  371. TempClient = BinlAllocateMemory(sizeof(CLIENT_STATE));
  372. if (TempClient == NULL) {
  373. InterlockedIncrement( &BinlGlobalClientLimit );
  374. BinlPrintDbg(( DEBUG_OSC_ERROR, "Could not get client state for %s\n", inet_ntoa(*(struct in_addr *)&RemoteIp) ));
  375. Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
  376. } else {
  377. TempClient->NegotiateProcessed = FALSE;
  378. TempClient->AuthenticateProcessed = FALSE;
  379. TempClient->LastSequenceNumber = 0;
  380. TempClient->LastResponse = NULL;
  381. TempClient->LastResponseAllocated = 0;
  382. TempClient->PositiveRefCount = 1;
  383. TempClient->NegativeRefCount = 0;
  384. TempClient->AuthenticatedDCLdapHandle = NULL;
  385. TempClient->UserToken = NULL;
  386. TempClient->LastUpdate = GetTickCount();
  387. TempClient->nCreateAccountCounter = 0;
  388. TempClient->nVariables = 0;
  389. //
  390. // Fill in some standard variables.
  391. //
  392. if (!OscInitializeClientVariables(TempClient)) {
  393. InterlockedIncrement( &BinlGlobalClientLimit );
  394. BinlPrintDbg(( DEBUG_OSC_ERROR, "Could not initialize client state for %s\n", inet_ntoa(*(struct in_addr *)&RemoteIp) ));
  395. BinlFreeMemory(TempClient);
  396. TempClient = NULL;
  397. Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
  398. } else {
  399. InitializeCriticalSection(&TempClient->CriticalSection);
  400. TempClient->CriticalSectionHeld = FALSE;
  401. TempClient->RemoteIp = RemoteIp;
  402. OscGenerateSeed(&TempClient->Seed);
  403. InsertHeadList(&ClientsQueue, &TempClient->Linkage);
  404. BinlPrintDbg(( DEBUG_OSC, "Allocating new client state for %s\n", inet_ntoa(*(struct in_addr *)&RemoteIp) ));
  405. }
  406. }
  407. }
  408. }
  409. if (TempClient) {
  410. //
  411. // Do a quick check to see if another client is using this. This
  412. // check is not synchronized with the setting of this variable to
  413. // FALSE, and it's possible two clients could slip through, but
  414. // that is OK since this is not fatal (each thread still needs
  415. // to actually get the critical section to do anything).
  416. //
  417. if (TempClient->CriticalSectionHeld && (!Remove)) {
  418. Error = ERROR_BUSY;
  419. TempClient = NULL;
  420. } else {
  421. ++TempClient->PositiveRefCount; // need to do this inside ClientsCriticalSection
  422. }
  423. }
  424. LeaveCriticalSection(&ClientsCriticalSection);
  425. *pClientState = TempClient;
  426. return Error;
  427. }
  428. VOID
  429. OscFreeClients(
  430. VOID
  431. )
  432. /*++
  433. Routine Description:
  434. This function frees the clients list for OS chooser. It is
  435. intended to be called only when the service is shutting down,
  436. so the critical section does not matter.
  437. Arguments:
  438. None.
  439. Return Value:
  440. None.
  441. --*/
  442. {
  443. PLIST_ENTRY p;
  444. PCLIENT_STATE TempClient;
  445. TraceFunc("OscFreeClients( )\n");
  446. while (!IsListEmpty(&ClientsQueue)) {
  447. p = RemoveHeadList(&ClientsQueue);
  448. TempClient = CONTAINING_RECORD(p, CLIENT_STATE, Linkage);
  449. FreeClient(TempClient);
  450. }
  451. }
  452. VOID
  453. SearchAndReplace(
  454. LPSAR psarList,
  455. LPSTR *pszString,
  456. DWORD ArraySize,
  457. DWORD dwSize,
  458. DWORD dwExtraSize )
  459. /*++
  460. Routine Description:
  461. Searches and replaces text in a ASCII (8-bit) string.
  462. Arguments:
  463. psarList - SearchAndReplace structure with the list of tokens and
  464. and the strings that are going replace the tokens.
  465. pszString - the text to search and replace.
  466. dwSize - length of the text in pszString.
  467. dwExtraSize - if the buffer is reallocated, how much extra room to allocate
  468. Return Value:
  469. None.
  470. --*/
  471. {
  472. LPSTR psz = *pszString;
  473. TraceFunc("SearchAndReplace( )\n");
  474. if ( !psarList || !*pszString )
  475. return;
  476. while ( *psz )
  477. {
  478. if ( *psz == '%' )
  479. {
  480. LPSAR psar = psarList;
  481. ULONG count = 0;
  482. LPSTR pszEnd;
  483. UCHAR ch;
  484. psz++; // move forward
  485. //
  486. // Find the end of the %MACRO%
  487. //
  488. pszEnd = psz;
  489. while ( *pszEnd && *pszEnd !='%' )
  490. pszEnd++;
  491. //
  492. // Terminate but remember the character (NULL or '%')
  493. //
  494. ch = *pszEnd;
  495. *pszEnd = 0;
  496. //
  497. // Loop trying to match the %MACRO% when a Token
  498. //
  499. while( count++ < ArraySize ) {
  500. if ( lstrcmpiA( psz, psar->pszToken ) == 0 )
  501. { // match, so replace
  502. DWORD dwString;
  503. DWORD dwToken;
  504. if ( psar->pszStringA == NULL )
  505. {
  506. // need to translate the string from UNICODE to ANSI
  507. DWORD dwLen;
  508. ANSI_STRING aString;
  509. UNICODE_STRING uString;
  510. uString.Buffer = psar->pszStringW;
  511. uString.Length = (USHORT)( wcslen( psar->pszStringW ) * sizeof(WCHAR) );
  512. dwLen = RtlUnicodeStringToAnsiSize(&uString); // includes NULL termination
  513. psar->pszStringA = BinlAllocateMemory( dwLen );
  514. if (psar->pszStringA == NULL) {
  515. BinlAssert( !"Out of memory!" );
  516. psar++;
  517. continue; // abort this replace
  518. }
  519. aString.Buffer = psar->pszStringA;
  520. aString.MaximumLength = (USHORT)dwLen;
  521. RtlUnicodeStringToAnsiString(
  522. &aString,
  523. &uString,
  524. FALSE);
  525. }
  526. dwString = strlen( psar->pszStringA );
  527. dwToken = strlen( psar->pszToken );
  528. psz--; // move back
  529. if ( 2 + dwToken < dwString )
  530. {
  531. // "%MACRO%" is smaller than "ReplaceString"
  532. // Check to see if we need to grow the buffer...
  533. LPSTR pszEnd = &psz[2 + dwToken];
  534. DWORD dwLenEnd = strlen( pszEnd ) + 1;
  535. DWORD dwLenBegin = (DWORD)( psz - *pszString );
  536. DWORD dwNewSize = dwLenBegin + dwString + dwLenEnd;
  537. //
  538. // Does the new string fit in the old space?
  539. //
  540. if ( dwSize < dwNewSize )
  541. {
  542. //
  543. // No. Make some space
  544. //
  545. LPSTR pszNewString;
  546. dwNewSize += 1024; // with some extra to grow
  547. pszNewString = BinlAllocateMemory( dwNewSize + dwExtraSize );
  548. if ( !pszNewString )
  549. {
  550. BinlAssert( !"Out of memory!" );
  551. return; // abort the rest
  552. }
  553. MoveMemory( pszNewString, *pszString, dwSize );
  554. dwSize = dwNewSize;
  555. psz = pszNewString + ( psz - *pszString );
  556. BinlFreeMemory( *pszString );
  557. *pszString = pszNewString;
  558. }
  559. MoveMemory( &psz[dwString], &psz[ 2 + dwToken ], dwLenEnd );
  560. }
  561. CopyMemory( psz, psar->pszStringA, dwString );
  562. if ( 2 + dwToken > dwString )
  563. {
  564. strcpy( &psz[ dwString ], &psz[ 2 + dwToken ] );
  565. }
  566. pszEnd = NULL; // match, NULL so we don't put the temp char back
  567. psz++; // move forward
  568. break;
  569. }
  570. psar++;
  571. }
  572. //
  573. // If no match, put the character back
  574. //
  575. if ( pszEnd != NULL )
  576. {
  577. *pszEnd = ch;
  578. }
  579. }
  580. else
  581. {
  582. psz++;
  583. }
  584. }
  585. }
  586. LPSTR
  587. FindSection(
  588. LPSTR sectionName,
  589. LPSTR sifFile
  590. )
  591. /*++
  592. Routine Description:
  593. This routine is for use by ProcessUniqueUdb. It scans in memory
  594. starting at sifFile, looking for a SIF section named "sectionName".
  595. Specifically it searches for a line whose first non-blank characters
  596. are "[sectionName]".
  597. Arguments:
  598. sectionName - The section name to look for, an ANSI string.
  599. sifFile - The SIF file in memory, which is NULL-terminated ANSI string.
  600. Return Value:
  601. A pointer to the start of the section -- the first character of the
  602. line after the one with [sectionName] in it.
  603. --*/
  604. {
  605. LPSTR curSifFile;
  606. DWORD lenSectionName;
  607. LPSTR foundSection = NULL;
  608. lenSectionName = strlen(sectionName);
  609. curSifFile = sifFile;
  610. while (*curSifFile != '\0') {
  611. //
  612. // At this point in the while, curSifFile points to the beginning
  613. // of a line.
  614. //
  615. //
  616. // First find the first non-blank character.
  617. //
  618. while ((*curSifFile != '\0') && (*curSifFile == ' ')) {
  619. ++curSifFile;
  620. }
  621. if (*curSifFile == '\0') {
  622. break;
  623. }
  624. if (*curSifFile == '[') {
  625. if ((memcmp(sectionName, curSifFile+1, lenSectionName) == 0) &&
  626. (curSifFile[lenSectionName+1] == ']')) {
  627. //
  628. // Found it, scan to start of next line.
  629. //
  630. while ((*curSifFile != '\0') && (*curSifFile != '\n')) {
  631. ++curSifFile;
  632. }
  633. if (*curSifFile == '\0') {
  634. break;
  635. }
  636. foundSection = curSifFile + 1; // +1 to skip past the '\n'
  637. break;
  638. }
  639. }
  640. //
  641. // Now scan to the start of the next line, defined as the
  642. // character after a \n.
  643. //
  644. while ((*curSifFile != L'\0') && (*curSifFile != L'\n')) {
  645. ++curSifFile;
  646. }
  647. if (*curSifFile == L'\0') {
  648. break;
  649. }
  650. ++curSifFile; // skip past the '\n'
  651. }
  652. return foundSection;
  653. }
  654. BOOLEAN
  655. FindLineInSection(
  656. PCHAR SectionStart,
  657. PWCHAR lineToMatch,
  658. PCHAR *existingLine,
  659. DWORD *existingLineLen
  660. )
  661. /*++
  662. Routine Description:
  663. This routine is for use by ProcessUniqueUdb. It scans in memory
  664. starting at SectionStart, which is assumed to be the first line of
  665. a section of a .sif file. It looks for a line that is for the same
  666. value as lineToMatch, which will be of the form "value=name".
  667. If it is found, it returns the line that it was found on.
  668. Arguments:
  669. SectionStart - The section of the .sif, in ANSI.
  670. lineToMatch - The value=name pair to match, in UNICODE.
  671. existingLine - Returns the existing line (in SectionStart), in ANSI.
  672. existingLineLen - Returns the length of the existing line, including
  673. final \r\n. Length is in characters, not bytes.
  674. Return Value:
  675. TRUE of the line is found, FALSE otherwise.
  676. --*/
  677. {
  678. LPWSTR endOfValue;
  679. LPSTR curSection;
  680. LPSTR curLine;
  681. LPSTR endOfLine;
  682. DWORD valueLength, ansiValueLength;
  683. BOOLEAN foundLine = FALSE;
  684. LPSTR valueToMatch = NULL;
  685. ANSI_STRING aString;
  686. UNICODE_STRING uString;
  687. //
  688. // First look at lineToMatch to see what we are looking
  689. // for. This is the text up to the first =, or all of it if there
  690. // is no =. Once found, we convert it to ANSI for comparisons.
  691. //
  692. endOfValue = wcschr(lineToMatch, L'=');
  693. if (endOfValue == NULL) {
  694. endOfValue = lineToMatch + wcslen(lineToMatch);
  695. }
  696. valueLength = (DWORD)(endOfValue - lineToMatch);
  697. //
  698. // Copy the sectionName to ANSI for comparisons.
  699. //
  700. uString.Buffer = lineToMatch;
  701. uString.Length = (USHORT)(valueLength*sizeof(WCHAR));
  702. ansiValueLength = RtlUnicodeStringToAnsiSize(&uString); // includes final '\0'
  703. valueToMatch = BinlAllocateMemory(ansiValueLength);
  704. if (valueToMatch == NULL) {
  705. return FALSE;
  706. }
  707. aString.Buffer = valueToMatch;
  708. aString.MaximumLength = (USHORT)ansiValueLength;
  709. RtlUnicodeStringToAnsiString(
  710. &aString,
  711. &uString,
  712. FALSE);
  713. --ansiValueLength; // remove final '\0' from the count
  714. //
  715. // now scan each line of SectionStart, until we find the beginning
  716. // of another section, a \0, or the matching line.
  717. //
  718. curSection = SectionStart;
  719. while (*curSection != '\0') {
  720. //
  721. // At this point in the while, curSection points to the beginning
  722. // of a line. Save the start of the current line.
  723. //
  724. curLine = curSection;
  725. //
  726. // First find the first non-blank character.
  727. //
  728. while ((*curSection != '\0') && (*curSection == ' ')) {
  729. ++curSection;
  730. }
  731. //
  732. // If we hit \0, we didn't find it.
  733. //
  734. if (*curSection == '\0') {
  735. break;
  736. }
  737. //
  738. // If we hit a line starting with [, we didn't find it.
  739. //
  740. if (*curSection == '[') {
  741. break;
  742. }
  743. //
  744. // If we hit a line starting with what we expect, followed
  745. // by an =, \0, or a blank, we found it.
  746. //
  747. if (strncmp(curSection, valueToMatch, ansiValueLength) == 0) {
  748. if ((curSection[ansiValueLength] == '=') ||
  749. (curSection[ansiValueLength] == '\0') ||
  750. (curSection[ansiValueLength] == ' ')) {
  751. *existingLine = curLine;
  752. endOfLine = strchr(curLine, '\n');
  753. if (endOfLine == NULL) {
  754. *existingLineLen = strlen(curLine);
  755. } else {
  756. *existingLineLen = (DWORD)(endOfLine + 1 - curLine);
  757. }
  758. foundLine = TRUE;
  759. break;
  760. }
  761. }
  762. //
  763. // Now scan to the start of the next line, defined as the
  764. // character after a \n.
  765. //
  766. while ((*curSection != L'\0') && (*curSection != L'\n')) {
  767. ++curSection;
  768. }
  769. if (*curSection == L'\0') {
  770. break;
  771. }
  772. ++curSection; // skip past the '\n'
  773. }
  774. if (valueToMatch != NULL) {
  775. BinlFreeMemory(valueToMatch);
  776. }
  777. return foundLine;
  778. }
  779. VOID
  780. ProcessUniqueUdb(
  781. LPSTR *sifFilePtr,
  782. DWORD sifFileLen,
  783. LPWSTR UniqueUdbPath,
  784. LPWSTR UniqueUdbId
  785. )
  786. /*++
  787. Routine Description:
  788. Overlays data from a unique.udb file based on the tag specified. The
  789. file to overlay on is in memory at *pszString. *pszString is reallocated
  790. if necessary.
  791. Arguments:
  792. sifFile - The file to overlay data on.
  793. sifFileLen - The current size of the data at *pszString (including final NULL).
  794. UniqueUdbPath - The path to the unique.udb file.
  795. UniqueUdbId - The ID in unique.udb to use.
  796. Return Value:
  797. None.
  798. --*/
  799. {
  800. PWCHAR TmpBuffer = NULL;
  801. DWORD len, sifFileAlloc, lineLen;
  802. LONG sizeToAdd;
  803. LPSTR sifFile = *sifFilePtr;
  804. PWCHAR sectionList = NULL;
  805. PWCHAR sectionLoc, sectionCur;
  806. PWCHAR sectionName = NULL;
  807. PCHAR ansiRealSectionName = NULL;
  808. PCHAR sectionStart;
  809. PWCHAR profileSectionCur;
  810. PWCHAR realSectionName;
  811. PCHAR existingLine;
  812. DWORD existingLineLen;
  813. DWORD lenRealSectionName;
  814. PCHAR insertionPoint;
  815. ANSI_STRING aString;
  816. UNICODE_STRING uString;
  817. #define TMP_BUFFER_SIZE 2048
  818. TraceFunc("ProcessUniqueUdb( )\n");
  819. TmpBuffer = BinlAllocateMemory(TMP_BUFFER_SIZE * sizeof(WCHAR));
  820. if (TmpBuffer == NULL) {
  821. return;
  822. }
  823. //
  824. // See if the ID appears in the [UniqueIds] section of the unique.udb file.
  825. //
  826. TmpBuffer[0] = L'\0';
  827. GetPrivateProfileString( L"UniqueIds", // section name
  828. UniqueUdbId, // line name
  829. L"", // default
  830. TmpBuffer,
  831. TMP_BUFFER_SIZE,
  832. UniqueUdbPath );
  833. if (TmpBuffer[0] == L'\0') {
  834. return;
  835. }
  836. //
  837. // sifFileAlloc is the size allocated for sifFile, whereas
  838. // sifFileLen is the amount actually used. They should only
  839. // be different while we are actively shuffling things
  840. // around.
  841. //
  842. sifFileAlloc = sifFileLen;
  843. //
  844. // Save the tmpbuffer result.
  845. //
  846. len = wcslen(TmpBuffer) + 1;
  847. sectionList = BinlAllocateMemory(len * sizeof(WCHAR));
  848. if (sectionList == NULL) {
  849. return;
  850. }
  851. wcscpy(sectionList, TmpBuffer);
  852. //
  853. // Now for each section identified in sectionList, scan for
  854. // the section to overlay.
  855. //
  856. sectionLoc = sectionList;
  857. while (TRUE) {
  858. //
  859. // First skip leading blanks
  860. //
  861. while (*sectionLoc && !iswalnum(*sectionLoc)) {
  862. //
  863. // Make sure we are not at a comment.
  864. //
  865. if (*sectionLoc == L';') {
  866. goto Cleanup;
  867. }
  868. ++sectionLoc;
  869. }
  870. if (!*sectionLoc) {
  871. goto Cleanup;
  872. }
  873. //
  874. // Now save sectionCur as the current section name
  875. // and skip to the end of it. Section names can be
  876. // any alphanumeric character, '.', or '_'.
  877. //
  878. sectionCur = sectionLoc;
  879. while((iswalnum(*sectionLoc)) ||
  880. (*sectionLoc == '.') ||
  881. (*sectionLoc == '_')) {
  882. ++sectionLoc;
  883. }
  884. //
  885. // Construct the new section name to look for. This will
  886. // be [UNIQUEUDBID:RealSectionName].
  887. //
  888. len = wcslen(UniqueUdbId) + (sectionLoc - sectionCur) + 2; // one for :, one for NULL
  889. sectionName = BinlAllocateMemory(len * sizeof(WCHAR));
  890. if (sectionName == NULL) {
  891. goto Cleanup;
  892. }
  893. wcscpy(sectionName, UniqueUdbId);
  894. wcscat(sectionName, L":");
  895. len = wcslen(sectionName);
  896. realSectionName = sectionName + len;
  897. memcpy(realSectionName, sectionCur, (sectionLoc - sectionCur) * sizeof(WCHAR));
  898. realSectionName[sectionLoc - sectionCur] = L'\0';
  899. //
  900. // Copy the sectionName to ANSI for comparisons.
  901. //
  902. uString.Buffer = realSectionName;
  903. uString.Length = (USHORT)(wcslen(realSectionName)*sizeof(WCHAR));
  904. lenRealSectionName = RtlUnicodeStringToAnsiSize(&uString); // includes final '\0'
  905. ansiRealSectionName = BinlAllocateMemory(lenRealSectionName);
  906. if (ansiRealSectionName == NULL) {
  907. goto Cleanup;
  908. }
  909. aString.Buffer = ansiRealSectionName;
  910. aString.MaximumLength = (USHORT)lenRealSectionName;
  911. RtlUnicodeStringToAnsiString(
  912. &aString,
  913. &uString,
  914. FALSE);
  915. --lenRealSectionName; // remove final '\0' from count
  916. //
  917. // See if there is a section with that name.
  918. //
  919. TmpBuffer[0] = L'\0';
  920. GetPrivateProfileSection( sectionName,
  921. TmpBuffer,
  922. TMP_BUFFER_SIZE,
  923. UniqueUdbPath );
  924. if (TmpBuffer[0] == L'\0') {
  925. continue;
  926. }
  927. //
  928. // Got the contents of the section, now process it.
  929. //
  930. sectionStart = FindSection(ansiRealSectionName, sifFile);
  931. sizeToAdd = 0; // amount we need to extend the buffer by.
  932. if (sectionStart == NULL) {
  933. //
  934. // No section, so need to add room for it.
  935. //
  936. // We put a CR-LF combo, then the section name in [], then
  937. // another CR-LF.
  938. //
  939. sizeToAdd = lenRealSectionName + 6;
  940. }
  941. //
  942. // Now scan through the entries in the profile section.
  943. //
  944. profileSectionCur = TmpBuffer;
  945. while (*profileSectionCur != L'\0') {
  946. uString.Buffer = profileSectionCur;
  947. uString.Length = (USHORT)(wcslen(profileSectionCur) * sizeof(WCHAR));
  948. //
  949. // Figure out how long profileSectionCur will be as an
  950. // ANSI string (may have DBCS data in it).
  951. //
  952. lineLen = RtlUnicodeStringToAnsiSize(&uString); // includes \0 termination
  953. --lineLen; // remove the \0 from the count
  954. //
  955. // If there is no existing section we have to add it;
  956. // if not, see if there is a line for this already.
  957. //
  958. if (sectionStart == NULL) {
  959. sizeToAdd += lineLen + 2; // +2 is for CR-LF
  960. } else {
  961. if (FindLineInSection(sectionStart,
  962. profileSectionCur,
  963. &existingLine,
  964. &existingLineLen)) {
  965. //
  966. // Need to remove current line.
  967. //
  968. memmove(existingLine, existingLine + existingLineLen,
  969. sifFileLen - ((existingLine - sifFile) + existingLineLen));
  970. sizeToAdd += lineLen + 2 - existingLineLen;
  971. sifFileLen -= existingLineLen;
  972. } else {
  973. sizeToAdd += lineLen + 2;
  974. }
  975. }
  976. profileSectionCur += wcslen(profileSectionCur) + 1;
  977. }
  978. //
  979. // Now we need to reallocate the buffer if needed.
  980. //
  981. if (sizeToAdd > 0) {
  982. //
  983. // No. Make some space
  984. //
  985. LPSTR pszNewString;
  986. pszNewString = BinlAllocateMemory( sifFileAlloc + sizeToAdd );
  987. if ( !pszNewString )
  988. {
  989. return; // abort the rest
  990. }
  991. MoveMemory( pszNewString, sifFile, sifFileLen);
  992. BinlFreeMemory( sifFile );
  993. //
  994. // Adjust sectionStart to be within the new buffer.
  995. //
  996. if (sectionStart != NULL) {
  997. sectionStart = pszNewString + (sectionStart - sifFile);
  998. }
  999. sifFile = pszNewString;
  1000. *sifFilePtr = pszNewString;
  1001. sifFileAlloc += sizeToAdd;
  1002. }
  1003. //
  1004. // Add the section header if necessary.
  1005. //
  1006. if (sectionStart == NULL) {
  1007. strcpy(sifFile + sifFileLen - 1, "\r\n[");
  1008. sifFileLen += 3;
  1009. strcpy(sifFile + sifFileLen - 1, ansiRealSectionName);
  1010. sifFileLen += lenRealSectionName;
  1011. strcpy(sifFile + sifFileLen - 1, "]\r\n");
  1012. sifFileLen += 3;
  1013. sectionStart = sifFile + sifFileLen - 1;
  1014. }
  1015. //
  1016. // Now add the items from the profile section. We know that
  1017. // they do not exist in the file and that we have reallocated
  1018. // the file buffer to be large enough.
  1019. //
  1020. profileSectionCur = TmpBuffer;
  1021. insertionPoint = sectionStart;
  1022. while (*profileSectionCur != L'\0') {
  1023. uString.Buffer = profileSectionCur;
  1024. uString.Length = (USHORT)(wcslen(profileSectionCur)*sizeof(WCHAR));
  1025. lineLen = RtlUnicodeStringToAnsiSize(&uString); // includes final '\0'
  1026. --lineLen; // remove final '\0' from count
  1027. //
  1028. // move anything we need to down and insert the new line.
  1029. //
  1030. memmove(insertionPoint + lineLen + 2, insertionPoint, sifFileLen - (insertionPoint - sifFile));
  1031. aString.Buffer = insertionPoint;
  1032. aString.MaximumLength = (USHORT)(lineLen+1);
  1033. RtlUnicodeStringToAnsiString(
  1034. &aString,
  1035. &uString,
  1036. FALSE);
  1037. memcpy(insertionPoint + lineLen, "\r\n", 2);
  1038. sifFileLen += lineLen + 2;
  1039. insertionPoint += lineLen + 2;
  1040. profileSectionCur += wcslen(profileSectionCur) + 1;
  1041. }
  1042. }
  1043. Cleanup:
  1044. if (sectionList != NULL) {
  1045. BinlFreeMemory(sectionList);
  1046. }
  1047. if (sectionName != NULL) {
  1048. BinlFreeMemory(sectionName);
  1049. }
  1050. if (ansiRealSectionName != NULL) {
  1051. BinlFreeMemory(ansiRealSectionName);
  1052. }
  1053. if (TmpBuffer != NULL) {
  1054. BinlFreeMemory(TmpBuffer);
  1055. }
  1056. ASSERT (sifFileLen <= sifFileAlloc);
  1057. }
  1058. //
  1059. // OscFindVariableA( )
  1060. //
  1061. LPSTR
  1062. OscFindVariableA(
  1063. PCLIENT_STATE clientState,
  1064. LPSTR variableName ) // variable name are always ASCII until OSChooser
  1065. // can handle Unicode.
  1066. {
  1067. ULONG i;
  1068. static CHAR szNullStringA[1] = { '\0' };
  1069. LPSTR overrideValue;
  1070. //
  1071. // First check to see if this a query we are supposed to override.
  1072. //
  1073. if (strcmp(variableName, "SIF") == 0) {
  1074. overrideValue = OscFindVariableA(clientState, "FORCESIFFILE");
  1075. if ((overrideValue != NULL) && (strlen(overrideValue) != 0)) {
  1076. return overrideValue;
  1077. }
  1078. }
  1079. for( i = 0; i < clientState->nVariables; i++ )
  1080. {
  1081. if ( strcmp( clientState->Variables[i].pszToken, variableName ) == 0 )
  1082. {
  1083. if ( clientState->Variables[i].pszStringA == NULL )
  1084. {
  1085. DWORD dwLen;
  1086. ANSI_STRING aString;
  1087. UNICODE_STRING uString;
  1088. uString.Buffer = clientState->Variables[i].pszStringW;
  1089. uString.Length = (USHORT)( wcslen( clientState->Variables[i].pszStringW ) * sizeof(WCHAR) );
  1090. dwLen = RtlUnicodeStringToAnsiSize( &uString ); // includes NULL termination
  1091. clientState->Variables[i].pszStringA = BinlAllocateMemory( dwLen );
  1092. if ( !(clientState->Variables[i].pszStringA) )
  1093. break; // out of memory
  1094. aString.Buffer = clientState->Variables[i].pszStringA;
  1095. aString.MaximumLength = (USHORT)dwLen;
  1096. RtlUnicodeStringToAnsiString(
  1097. &aString,
  1098. &uString,
  1099. FALSE);
  1100. }
  1101. return clientState->Variables[i].pszStringA;
  1102. }
  1103. }
  1104. return szNullStringA;
  1105. }
  1106. //
  1107. // OscFindVariableW( )
  1108. //
  1109. LPWSTR
  1110. OscFindVariableW(
  1111. PCLIENT_STATE clientState,
  1112. LPSTR variableName ) // variable name are always ASCII until OSChooser
  1113. // can handle Unicode.
  1114. {
  1115. ULONG i;
  1116. static WCHAR szNullStringW[1] = { L'\0' };
  1117. LPWSTR overrideValue;
  1118. //
  1119. // First check to see if this a query we are supposed to override.
  1120. //
  1121. if (strcmp(variableName, "SIF") == 0) {
  1122. overrideValue = OscFindVariableW(clientState, "FORCESIFFILE");
  1123. if ((overrideValue != NULL) && (wcslen(overrideValue) != 0)) {
  1124. return overrideValue;
  1125. }
  1126. }
  1127. for( i = 0; i < clientState->nVariables; i++ )
  1128. {
  1129. if ( strcmp( clientState->Variables[i].pszToken, variableName ) == 0 )
  1130. {
  1131. if ( clientState->Variables[i].pszStringW == NULL )
  1132. {
  1133. DWORD dwLen = _mbslen( clientState->Variables[i].pszStringA ) + 1;
  1134. ANSI_STRING aString;
  1135. UNICODE_STRING uString;
  1136. clientState->Variables[i].pszStringW = BinlAllocateMemory( dwLen * sizeof(WCHAR) );
  1137. if ( !(clientState->Variables[i].pszStringW) )
  1138. break; // out of memory
  1139. uString.Buffer = clientState->Variables[i].pszStringW;
  1140. uString.MaximumLength = (USHORT)(dwLen * sizeof(WCHAR));
  1141. aString.Buffer = clientState->Variables[i].pszStringA;
  1142. aString.Length = (USHORT)strlen( clientState->Variables[i].pszStringA );
  1143. RtlAnsiStringToUnicodeString(
  1144. &uString,
  1145. &aString,
  1146. FALSE);
  1147. }
  1148. return clientState->Variables[i].pszStringW;
  1149. }
  1150. }
  1151. return szNullStringW;
  1152. }
  1153. //
  1154. // OscCheckVariableLength( )
  1155. //
  1156. BOOLEAN
  1157. OscCheckVariableLength(
  1158. PCLIENT_STATE clientState,
  1159. LPSTR variableName,
  1160. ULONG variableLength )
  1161. {
  1162. ULONG i;
  1163. for (i = 0; i < OSC_VARIABLE_MAXIMUM_COUNT; i++) {
  1164. if (strcmp(OscMaximums[i].VariableName, variableName) == 0) {
  1165. if (variableLength > OscMaximums[i].MaximumLength) {
  1166. BinlPrintDbg((DEBUG_OSC_ERROR, "Variable %s was %d bytes, only %d allowed\n",
  1167. variableName,
  1168. variableLength,
  1169. OscMaximums[i].MaximumLength));
  1170. OscAddVariableA( clientState, "SUBERROR", variableName );
  1171. return FALSE;
  1172. } else {
  1173. return TRUE;
  1174. }
  1175. }
  1176. }
  1177. //
  1178. // If we don't find it in our list of maximums, it is OK.
  1179. //
  1180. return TRUE;
  1181. }
  1182. //
  1183. // OscAddVariableA( )
  1184. //
  1185. DWORD
  1186. OscAddVariableA(
  1187. PCLIENT_STATE clientState,
  1188. LPSTR variableName, // variable name are always ASCII until OSChooser
  1189. // can handle Unicode.
  1190. LPSTR variableValue )
  1191. {
  1192. ULONG i;
  1193. if ( variableValue == NULL )
  1194. return E_POINTER; // no value to add... abort
  1195. if (!OscCheckVariableLength(clientState, variableName, strlen(variableValue))) {
  1196. return E_INVALIDARG;
  1197. }
  1198. for( i = 0; i < clientState->nVariables; i++ )
  1199. {
  1200. if ( strcmp( clientState->Variables[i].pszToken, variableName ) == 0 )
  1201. {
  1202. ULONG l = strlen( variableValue );
  1203. if ( clientState->Variables[i].pszStringW != NULL )
  1204. {
  1205. BinlFreeMemory( clientState->Variables[i].pszStringW );
  1206. clientState->Variables[i].pszStringW = NULL;
  1207. }
  1208. if ( clientState->Variables[i].pszStringA != NULL )
  1209. { // found a previous one
  1210. // Don't replace values with ""
  1211. if ( variableValue[0] == '\0' ) {
  1212. break;
  1213. } else {
  1214. // replace it with the new one
  1215. if ( l <= strlen( clientState->Variables[i].pszStringA ) )
  1216. {
  1217. strcpy( clientState->Variables[i].pszStringA, variableValue );
  1218. }
  1219. else
  1220. {
  1221. // need more space, delete the old
  1222. BinlFreeMemory( clientState->Variables[i].pszStringA );
  1223. clientState->Variables[i].pszStringA = NULL;
  1224. }
  1225. }
  1226. }
  1227. break;
  1228. }
  1229. }
  1230. //
  1231. // Limit the number of variables we can have. Everything else is ignored.
  1232. //
  1233. if ( clientState->nVariables == MAX_VARIABLES &&
  1234. clientState->nVariables == i )
  1235. return E_OUTOFMEMORY;
  1236. //
  1237. // Adding a new one
  1238. //
  1239. if ( clientState->nVariables == i )
  1240. {
  1241. clientState->Variables[i].pszToken = BinlStrDupA( variableName );
  1242. if (clientState->Variables[i].pszToken == NULL) {
  1243. return E_OUTOFMEMORY;
  1244. }
  1245. clientState->nVariables++;
  1246. }
  1247. //
  1248. // If this is a new one or a new Value for an existing variable
  1249. // that does not fit in the old values space, create a dup of the
  1250. // value.
  1251. //
  1252. if ( clientState->Variables[i].pszStringA == NULL )
  1253. {
  1254. BinlAssert( variableValue != NULL );
  1255. clientState->Variables[i].pszStringA = BinlStrDupA( variableValue );
  1256. if (clientState->Variables[i].pszStringA == NULL) {
  1257. if ((i + 1) == clientState->nVariables) {
  1258. clientState->nVariables--;
  1259. BinlFreeMemory(clientState->Variables[i].pszToken);
  1260. }
  1261. return E_OUTOFMEMORY;
  1262. }
  1263. //
  1264. // The "OPTIONS" variable can have a lot of stuff in it and will
  1265. // blow up the BinlPrint(). Just avoid the whole mess by not
  1266. // printing it
  1267. //
  1268. if ( lstrcmpA( clientState->Variables[i].pszToken, "OPTIONS" ) != 0 ) {
  1269. BinlPrintDbg((DEBUG_OSC, "Add Var:'%s' = '%s'\n",
  1270. clientState->Variables[i].pszToken,
  1271. clientState->Variables[i].pszStringA ));
  1272. }
  1273. }
  1274. //
  1275. // it will be converted to UNICODE when OscFindVariableW( ) is called
  1276. //
  1277. return ERROR_SUCCESS;
  1278. }
  1279. //
  1280. // OscAddVariableW( )
  1281. //
  1282. DWORD
  1283. OscAddVariableW(
  1284. PCLIENT_STATE clientState,
  1285. LPSTR variableName, // variable name are always ASCII until OSChooser
  1286. // can handle Unicode.
  1287. LPWSTR variableValue )
  1288. {
  1289. ULONG i;
  1290. if ( variableValue == NULL )
  1291. return E_POINTER; // no value to add... abort
  1292. if (!OscCheckVariableLength(clientState, variableName, wcslen(variableValue))) {
  1293. return E_INVALIDARG;
  1294. }
  1295. for( i = 0; i < clientState->nVariables; i++ )
  1296. {
  1297. if ( strcmp( clientState->Variables[i].pszToken, variableName ) == 0 )
  1298. {
  1299. if ( clientState->Variables[i].pszStringA != NULL )
  1300. {
  1301. BinlFreeMemory( clientState->Variables[i].pszStringA );
  1302. clientState->Variables[i].pszStringA = NULL;
  1303. }
  1304. if ( clientState->Variables[i].pszStringW != NULL )
  1305. { // found a previous one
  1306. // Don't replace values with ""
  1307. if ( variableValue[0] == L'\0' ) {
  1308. break;
  1309. } else {
  1310. // replace it with the new one
  1311. ULONG Length = wcslen( variableValue );
  1312. if ( Length < wcslen( clientState->Variables[i].pszStringW ) )
  1313. {
  1314. wcscpy( clientState->Variables[i].pszStringW, variableValue );
  1315. }
  1316. else
  1317. {
  1318. // need more space, delete the old
  1319. BinlFreeMemory( clientState->Variables[i].pszStringW );
  1320. clientState->Variables[i].pszStringW = NULL;
  1321. }
  1322. }
  1323. }
  1324. break;
  1325. }
  1326. }
  1327. //
  1328. // Limit the number of variables we can have. Everything else is ignored.
  1329. //
  1330. if ( clientState->nVariables == MAX_VARIABLES &&
  1331. clientState->nVariables == i )
  1332. return E_OUTOFMEMORY; // out of space
  1333. //
  1334. // Adding a new one
  1335. //
  1336. if ( clientState->nVariables == i )
  1337. {
  1338. clientState->Variables[i].pszToken = BinlStrDupA( variableName );
  1339. if (clientState->Variables[i].pszToken == NULL) {
  1340. return E_OUTOFMEMORY;
  1341. }
  1342. clientState->nVariables++;
  1343. }
  1344. //
  1345. // If this is a new one or a new Value for an existing variable
  1346. // that does not fit in the old values space, create a dup of the
  1347. // value.
  1348. //
  1349. if ( clientState->Variables[i].pszStringW == NULL )
  1350. {
  1351. BinlAssert( variableValue != NULL );
  1352. clientState->Variables[i].pszStringW = BinlStrDupW( variableValue);
  1353. if (clientState->Variables[i].pszStringW == NULL) {
  1354. if ((i + 1) == clientState->nVariables) {
  1355. clientState->nVariables--;
  1356. BinlFreeMemory(clientState->Variables[i].pszToken);
  1357. }
  1358. return E_OUTOFMEMORY;
  1359. }
  1360. BinlPrintDbg((DEBUG_OSC, "Add Var:'%s' = '%ws'\n",
  1361. clientState->Variables[i].pszToken,
  1362. clientState->Variables[i].pszStringW ));
  1363. }
  1364. //
  1365. // it will be converted to ASCII when OscFindVariableA( ) is called
  1366. //
  1367. return ERROR_SUCCESS;
  1368. }
  1369. //
  1370. // OscResetVariable( )
  1371. //
  1372. VOID
  1373. OscResetVariable(
  1374. PCLIENT_STATE clientState,
  1375. LPSTR variableName
  1376. )
  1377. {
  1378. ULONG i;
  1379. BOOLEAN found = FALSE;
  1380. for( i = 0; i < clientState->nVariables; i++ ) {
  1381. if ( strcmp( clientState->Variables[i].pszToken, variableName ) == 0 ) {
  1382. if ( clientState->Variables[i].pszStringA != NULL ) {
  1383. BinlFreeMemory( clientState->Variables[i].pszStringA );
  1384. clientState->Variables[i].pszStringA = NULL;
  1385. }
  1386. if ( clientState->Variables[i].pszStringW != NULL ) { // found a previous one
  1387. BinlFreeMemory( clientState->Variables[i].pszStringW );
  1388. clientState->Variables[i].pszStringW = NULL;
  1389. }
  1390. BinlPrintDbg((DEBUG_OSC, "Deleted Var:'%s'\n",
  1391. clientState->Variables[i].pszToken ));
  1392. BinlFreeMemory( clientState->Variables[i].pszToken );
  1393. found = TRUE;
  1394. break;
  1395. }
  1396. }
  1397. if (found) {
  1398. //
  1399. // move all existing ones up.
  1400. //
  1401. while (i < clientState->nVariables) {
  1402. clientState->Variables[i].pszToken = clientState->Variables[i+1].pszToken;
  1403. clientState->Variables[i].pszStringA = clientState->Variables[i+1].pszStringA;
  1404. clientState->Variables[i].pszStringW = clientState->Variables[i+1].pszStringW;
  1405. i++;
  1406. }
  1407. clientState->nVariables--;
  1408. }
  1409. return;
  1410. }