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.

1882 lines
54 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. //
  202. // if this client had its name generated,
  203. // then attempt to remove the name from the queued DS list
  204. // this behavior is necessary for when a client times out
  205. //
  206. if (client->fAutomaticMachineName) {
  207. PWCHAR pMachineName; // Pointer to Machine Name variable value
  208. DWORD Error;
  209. pMachineName = OscFindVariableW( client, "MACHINENAME" );
  210. Error = RemoveQueuedDSName(pMachineName);
  211. if (Error != ERROR_SUCCESS) {
  212. BinlPrintDbg(( DEBUG_ERRORS, "RemoveQueuedDSName returned with status: 0x%x\n", Error));
  213. if (Error == ERROR_NOT_FOUND) {
  214. BinlPrintDbg(( DEBUG_ERRORS, "QueuedDSName already removed\n", Error));
  215. Error = ERROR_SUCCESS;
  216. }
  217. }
  218. }
  219. BinlPrintDbg(( DEBUG_OSC, "Freeing client state for %s\n", inet_ntoa(*(struct in_addr *)&(client->RemoteIp)) ));
  220. DeleteCriticalSection(&client->CriticalSection);
  221. if (client->LastResponse)
  222. BinlFreeMemory(client->LastResponse);
  223. OscFreeClientVariables(client);
  224. InterlockedIncrement( &BinlGlobalClientLimit );
  225. if (client->NegotiateProcessed) {
  226. DeleteSecurityContext( &client->ServerContextHandle );
  227. }
  228. if (client->AuthenticatedDCLdapHandle) {
  229. ldap_unbind(client->AuthenticatedDCLdapHandle);
  230. }
  231. if (client->UserToken) {
  232. CloseHandle(client->UserToken);
  233. }
  234. BinlFreeMemory(client);
  235. }
  236. VOID
  237. OscFreeClientVariables(
  238. PCLIENT_STATE clientState
  239. )
  240. /*++
  241. Routine Description:
  242. This function frees all variables in a client state.
  243. Arguments:
  244. clientState - the client state pointer.
  245. Return Value:
  246. None.
  247. --*/
  248. {
  249. ULONG i;
  250. for( i = 0; i < clientState->nVariables; i++ )
  251. {
  252. BinlFreeMemory(clientState->Variables[i].pszToken);
  253. if (clientState->Variables[i].pszStringA) {
  254. BinlFreeMemory(clientState->Variables[i].pszStringA);
  255. clientState->Variables[i].pszStringA = NULL;
  256. }
  257. if (clientState->Variables[i].pszStringW) {
  258. BinlFreeMemory(clientState->Variables[i].pszStringW);
  259. clientState->Variables[i].pszStringW = NULL;
  260. }
  261. }
  262. clientState->nVariables = 0;
  263. clientState->fHaveSetupMachineDN = FALSE;
  264. clientState->fCreateNewAccount = FALSE;
  265. clientState->fAutomaticMachineName = FALSE;
  266. }
  267. BOOLEAN
  268. OscInitializeClientVariables(
  269. PCLIENT_STATE clientState
  270. )
  271. /*++
  272. Routine Description:
  273. This function cleans out any variables in the client state,
  274. then initializes some default values, which may later be
  275. overwritten by variables from client screens.
  276. This function is called when a client state is created, and
  277. when it is re-used from cache.
  278. Arguments:
  279. clientState - the client state pointer.
  280. Return Value:
  281. None.
  282. --*/
  283. {
  284. BOOLEAN retVal = TRUE;
  285. SYSTEMTIME SystemTime;
  286. FILETIME FileTime;
  287. WCHAR pTime[64];
  288. //
  289. // First clean out any variables in the client state.
  290. //
  291. OscFreeClientVariables(clientState);
  292. //
  293. // Now add the variables.
  294. //
  295. EnterCriticalSection( &gcsParameters );
  296. if (BinlGlobalDefaultLanguage) {
  297. OscAddVariableW( clientState, "LANGUAGE", BinlGlobalDefaultLanguage );
  298. } else {
  299. OscAddVariableW( clientState, "LANGUAGE", DEFAULT_LANGUAGE );
  300. }
  301. if (BinlGlobalDefaultOrgname) {
  302. OscAddVariableW( clientState, "ORGNAME", BinlGlobalDefaultOrgname );
  303. } else {
  304. OscAddVariableW( clientState, "ORGNAME", DEFAULT_ORGNAME );
  305. }
  306. if (BinlGlobalDefaultTimezone) {
  307. OscAddVariableW( clientState, "TIMEZONE", BinlGlobalDefaultTimezone );
  308. } else {
  309. OscAddVariableW( clientState, "TIMEZONE", DEFAULT_TIMEZONE );
  310. }
  311. if (BinlGlobalOurDomainName == NULL || BinlGlobalOurServerName == NULL) {
  312. LeaveCriticalSection( &gcsParameters );
  313. BinlPrintDbg((DEBUG_OSC_ERROR, "!! Error we don't have a FQDN for ourselves.\n" ));
  314. retVal = FALSE;
  315. goto Cleanup;
  316. }
  317. OscAddVariableW( clientState, "SERVERDOMAIN", BinlGlobalOurDomainName );
  318. // Add the Server's name variable
  319. OscAddVariableW( clientState, "SERVERNAME", BinlGlobalOurServerName );
  320. GetSystemTime(&SystemTime);
  321. if (SystemTimeToFileTime(&SystemTime,&FileTime)) {
  322. swprintf(pTime,L"%d;%d",FileTime.dwHighDateTime,FileTime.dwLowDateTime);
  323. OscAddVariableW( clientState, "ServerUTCFileTime", pTime );
  324. }
  325. OscAddVariableW(
  326. clientState,
  327. "NTLMV2Enabled",
  328. BinlGlobalUseNTLMV2
  329. ? L"1"
  330. : L"0" );
  331. LeaveCriticalSection( &gcsParameters );
  332. OscAddVariableA( clientState, "SUBERROR", " " );
  333. clientState->fHaveSetupMachineDN = FALSE;
  334. clientState->fCreateNewAccount = FALSE;
  335. clientState->fAutomaticMachineName = FALSE;
  336. clientState->InitializeOnFirstRequest = FALSE;
  337. Cleanup:
  338. //
  339. // If this fails, clean up anything we set here.
  340. //
  341. if (!retVal) {
  342. OscFreeClientVariables(clientState);
  343. }
  344. return retVal;
  345. }
  346. DWORD
  347. OscFindClient(
  348. ULONG RemoteIp,
  349. BOOL Remove,
  350. PCLIENT_STATE * pClientState
  351. )
  352. /*++
  353. Routine Description:
  354. This function looks up a client in our client database, using
  355. their IP address. If Remove is TRUE, it removes the entry if
  356. found. Otherwise, if not found, it creates a new entry.
  357. Arguments:
  358. RemoteIp - the remote IP address.
  359. Remove - TRUE if the client should be removed if found.
  360. pClientState - Returns the CLIENT_STATE.
  361. Return Value:
  362. ERROR_SUCCESS if it finds the client and it is not in use.
  363. ERROR_NOT_ENOUGH_SERVER_MEMORY if a client state could not be allocated.
  364. ERROR_BUSY if the client state is already being used by another thread.
  365. --*/
  366. {
  367. LONG oldCount;
  368. PLIST_ENTRY p;
  369. DWORD Error = ERROR_SUCCESS;
  370. PCLIENT_STATE TempClient = NULL;
  371. TraceFunc("OscFindClient( )\n");
  372. EnterCriticalSection(&ClientsCriticalSection);
  373. for (p = ClientsQueue.Flink;
  374. p != &ClientsQueue;
  375. p = p->Flink) {
  376. TempClient = CONTAINING_RECORD(p, CLIENT_STATE, Linkage);
  377. if (TempClient->RemoteIp == RemoteIp) {
  378. //
  379. // Found it!
  380. //
  381. if (Remove) {
  382. RemoveEntryList(&TempClient->Linkage);
  383. TraceFunc("Client removed.\n");
  384. }
  385. break;
  386. }
  387. }
  388. if (p == &ClientsQueue) {
  389. TempClient = NULL;
  390. }
  391. if (!TempClient && (!Remove)) {
  392. //
  393. // Not found, allocate a new one.
  394. //
  395. oldCount = InterlockedDecrement( &BinlGlobalClientLimit );
  396. if (oldCount <= 0) {
  397. InterlockedIncrement( &BinlGlobalClientLimit );
  398. BinlPrintDbg(( DEBUG_OSC_ERROR, "Way too many clients, 0x%x clients\n", BINL_MAX_CLIENT_RECORDS));
  399. Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
  400. TempClient = NULL;
  401. } else {
  402. TraceFunc("Creating new client...\n");
  403. TempClient = BinlAllocateMemory(sizeof(CLIENT_STATE));
  404. if (TempClient == NULL) {
  405. InterlockedIncrement( &BinlGlobalClientLimit );
  406. BinlPrintDbg(( DEBUG_OSC_ERROR, "Could not get client state for %s\n", inet_ntoa(*(struct in_addr *)&RemoteIp) ));
  407. Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
  408. } else {
  409. TempClient->NegotiateProcessed = FALSE;
  410. TempClient->AuthenticateProcessed = FALSE;
  411. TempClient->LastSequenceNumber = 0;
  412. TempClient->LastResponse = NULL;
  413. TempClient->LastResponseAllocated = 0;
  414. TempClient->PositiveRefCount = 1;
  415. TempClient->NegativeRefCount = 0;
  416. TempClient->AuthenticatedDCLdapHandle = NULL;
  417. TempClient->UserToken = NULL;
  418. TempClient->LastUpdate = GetTickCount();
  419. TempClient->nCreateAccountCounter = 0;
  420. TempClient->nVariables = 0;
  421. //
  422. // Fill in some standard variables.
  423. //
  424. if (!OscInitializeClientVariables(TempClient)) {
  425. InterlockedIncrement( &BinlGlobalClientLimit );
  426. BinlPrintDbg(( DEBUG_OSC_ERROR, "Could not initialize client state for %s\n", inet_ntoa(*(struct in_addr *)&RemoteIp) ));
  427. BinlFreeMemory(TempClient);
  428. TempClient = NULL;
  429. Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
  430. } else {
  431. InitializeCriticalSection(&TempClient->CriticalSection);
  432. TempClient->CriticalSectionHeld = FALSE;
  433. TempClient->RemoteIp = RemoteIp;
  434. OscGenerateSeed(&TempClient->Seed);
  435. InsertHeadList(&ClientsQueue, &TempClient->Linkage);
  436. BinlPrintDbg(( DEBUG_OSC, "Allocating new client state for %s\n", inet_ntoa(*(struct in_addr *)&RemoteIp) ));
  437. }
  438. }
  439. }
  440. }
  441. if (TempClient) {
  442. //
  443. // Do a quick check to see if another client is using this. This
  444. // check is not synchronized with the setting of this variable to
  445. // FALSE, and it's possible two clients could slip through, but
  446. // that is OK since this is not fatal (each thread still needs
  447. // to actually get the critical section to do anything).
  448. //
  449. if (TempClient->CriticalSectionHeld && (!Remove)) {
  450. Error = ERROR_BUSY;
  451. TempClient = NULL;
  452. } else {
  453. ++TempClient->PositiveRefCount; // need to do this inside ClientsCriticalSection
  454. }
  455. }
  456. LeaveCriticalSection(&ClientsCriticalSection);
  457. *pClientState = TempClient;
  458. return Error;
  459. }
  460. VOID
  461. OscFreeClients(
  462. VOID
  463. )
  464. /*++
  465. Routine Description:
  466. This function frees the clients list for OS chooser. It is
  467. intended to be called only when the service is shutting down,
  468. so the critical section does not matter.
  469. Arguments:
  470. None.
  471. Return Value:
  472. None.
  473. --*/
  474. {
  475. PLIST_ENTRY p;
  476. PCLIENT_STATE TempClient;
  477. TraceFunc("OscFreeClients( )\n");
  478. while (!IsListEmpty(&ClientsQueue)) {
  479. p = RemoveHeadList(&ClientsQueue);
  480. TempClient = CONTAINING_RECORD(p, CLIENT_STATE, Linkage);
  481. FreeClient(TempClient);
  482. }
  483. }
  484. VOID
  485. SearchAndReplace(
  486. LPSAR psarList,
  487. LPSTR *pszString,
  488. DWORD ArraySize,
  489. DWORD dwSize,
  490. DWORD dwExtraSize )
  491. /*++
  492. Routine Description:
  493. Searches and replaces text in a ASCII (8-bit) string.
  494. Arguments:
  495. psarList - SearchAndReplace structure with the list of tokens and
  496. and the strings that are going replace the tokens.
  497. pszString - the text to search and replace.
  498. dwSize - length of the text in pszString.
  499. dwExtraSize - if the buffer is reallocated, how much extra room to allocate
  500. Return Value:
  501. None.
  502. --*/
  503. {
  504. LPSTR psz = *pszString;
  505. TraceFunc("SearchAndReplace( )\n");
  506. if ( !psarList || !*pszString )
  507. return;
  508. while ( *psz )
  509. {
  510. if ( *psz == '%' )
  511. {
  512. LPSAR psar = psarList;
  513. ULONG count = 0;
  514. LPSTR pszEnd;
  515. UCHAR ch;
  516. psz++; // move forward
  517. //
  518. // Find the end of the %MACRO%
  519. //
  520. pszEnd = psz;
  521. while ( *pszEnd && *pszEnd !='%' )
  522. pszEnd++;
  523. //
  524. // Terminate but remember the character (NULL or '%')
  525. //
  526. ch = *pszEnd;
  527. *pszEnd = 0;
  528. //
  529. // Loop trying to match the %MACRO% when a Token
  530. //
  531. while( count++ < ArraySize ) {
  532. if ( _stricmp( psz, psar->pszToken ) == 0 )
  533. { // match, so replace
  534. DWORD dwString;
  535. DWORD dwToken;
  536. if ( psar->pszStringA == NULL )
  537. {
  538. // need to translate the string from UNICODE to ANSI
  539. DWORD dwLen;
  540. ANSI_STRING aString;
  541. UNICODE_STRING uString;
  542. uString.Buffer = psar->pszStringW;
  543. uString.Length = (USHORT)( wcslen( psar->pszStringW ) * sizeof(WCHAR) );
  544. dwLen = RtlUnicodeStringToAnsiSize(&uString); // includes NULL termination
  545. psar->pszStringA = BinlAllocateMemory( dwLen );
  546. if (psar->pszStringA == NULL) {
  547. BinlAssert( !"Out of memory!" );
  548. psar++;
  549. continue; // abort this replace
  550. }
  551. aString.Buffer = psar->pszStringA;
  552. aString.MaximumLength = (USHORT)dwLen;
  553. RtlUnicodeStringToAnsiString(
  554. &aString,
  555. &uString,
  556. FALSE);
  557. }
  558. dwString = strlen( psar->pszStringA );
  559. dwToken = strlen( psar->pszToken );
  560. psz--; // move back
  561. if ( 2 + dwToken < dwString )
  562. {
  563. // "%MACRO%" is smaller than "ReplaceString"
  564. // Check to see if we need to grow the buffer...
  565. LPSTR pszEndBuff = &psz[2 + dwToken];
  566. DWORD dwLenEnd = strlen( pszEndBuff ) + 1;
  567. DWORD dwLenBegin = (DWORD)( psz - *pszString );
  568. DWORD dwNewSize = dwLenBegin + dwString + dwLenEnd;
  569. //
  570. // Does the new string fit in the old space?
  571. //
  572. if ( dwSize < dwNewSize )
  573. {
  574. //
  575. // No. Make some space
  576. //
  577. LPSTR pszNewString;
  578. dwNewSize += 1024; // with some extra to grow
  579. pszNewString = BinlAllocateMemory( dwNewSize + dwExtraSize );
  580. if ( !pszNewString )
  581. {
  582. BinlAssert( !"Out of memory!" );
  583. return; // abort the rest
  584. }
  585. MoveMemory( pszNewString, *pszString, dwSize );
  586. dwSize = dwNewSize;
  587. psz = pszNewString + ( psz - *pszString );
  588. BinlFreeMemory( *pszString );
  589. *pszString = pszNewString;
  590. }
  591. MoveMemory( &psz[dwString], &psz[ 2 + dwToken ], dwLenEnd );
  592. }
  593. CopyMemory( psz, psar->pszStringA, dwString );
  594. if ( 2 + dwToken > dwString )
  595. {
  596. strcpy( &psz[ dwString ], &psz[ 2 + dwToken ] );
  597. }
  598. pszEnd = NULL; // match, NULL so we don't put the temp char back
  599. psz++; // move forward
  600. break;
  601. }
  602. psar++;
  603. }
  604. //
  605. // If no match, put the character back
  606. //
  607. if ( pszEnd != NULL )
  608. {
  609. *pszEnd = ch;
  610. }
  611. }
  612. else
  613. {
  614. psz++;
  615. }
  616. }
  617. }
  618. LPSTR
  619. FindSection(
  620. LPSTR sectionName,
  621. LPSTR sifFile
  622. )
  623. /*++
  624. Routine Description:
  625. This routine is for use by ProcessUniqueUdb. It scans in memory
  626. starting at sifFile, looking for a SIF section named "sectionName".
  627. Specifically it searches for a line whose first non-blank characters
  628. are "[sectionName]".
  629. Arguments:
  630. sectionName - The section name to look for, an ANSI string.
  631. sifFile - The SIF file in memory, which is NULL-terminated ANSI string.
  632. Return Value:
  633. A pointer to the start of the section -- the first character of the
  634. line after the one with [sectionName] in it.
  635. --*/
  636. {
  637. LPSTR curSifFile;
  638. DWORD lenSectionName;
  639. LPSTR foundSection = NULL;
  640. lenSectionName = strlen(sectionName);
  641. curSifFile = sifFile;
  642. while (*curSifFile != '\0') {
  643. //
  644. // At this point in the while, curSifFile points to the beginning
  645. // of a line.
  646. //
  647. //
  648. // First find the first non-blank character.
  649. //
  650. while ((*curSifFile != '\0') && (*curSifFile == ' ')) {
  651. ++curSifFile;
  652. }
  653. if (*curSifFile == '\0') {
  654. break;
  655. }
  656. if (*curSifFile == '[') {
  657. if ((memcmp(sectionName, curSifFile+1, lenSectionName) == 0) &&
  658. (curSifFile[lenSectionName+1] == ']')) {
  659. //
  660. // Found it, scan to start of next line.
  661. //
  662. while ((*curSifFile != '\0') && (*curSifFile != '\n')) {
  663. ++curSifFile;
  664. }
  665. if (*curSifFile == '\0') {
  666. break;
  667. }
  668. foundSection = curSifFile + 1; // +1 to skip past the '\n'
  669. break;
  670. }
  671. }
  672. //
  673. // Now scan to the start of the next line, defined as the
  674. // character after a \n.
  675. //
  676. while ((*curSifFile != L'\0') && (*curSifFile != L'\n')) {
  677. ++curSifFile;
  678. }
  679. if (*curSifFile == L'\0') {
  680. break;
  681. }
  682. ++curSifFile; // skip past the '\n'
  683. }
  684. return foundSection;
  685. }
  686. BOOLEAN
  687. FindLineInSection(
  688. PCHAR SectionStart,
  689. PWCHAR lineToMatch,
  690. PCHAR *existingLine,
  691. DWORD *existingLineLen
  692. )
  693. /*++
  694. Routine Description:
  695. This routine is for use by ProcessUniqueUdb. It scans in memory
  696. starting at SectionStart, which is assumed to be the first line of
  697. a section of a .sif file. It looks for a line that is for the same
  698. value as lineToMatch, which will be of the form "value=name".
  699. If it is found, it returns the line that it was found on.
  700. Arguments:
  701. SectionStart - The section of the .sif, in ANSI.
  702. lineToMatch - The value=name pair to match, in UNICODE.
  703. existingLine - Returns the existing line (in SectionStart), in ANSI.
  704. existingLineLen - Returns the length of the existing line, including
  705. final \r\n. Length is in characters, not bytes.
  706. Return Value:
  707. TRUE of the line is found, FALSE otherwise.
  708. --*/
  709. {
  710. LPWSTR endOfValue;
  711. LPSTR curSection;
  712. LPSTR curLine;
  713. LPSTR endOfLine;
  714. DWORD valueLength, ansiValueLength;
  715. BOOLEAN foundLine = FALSE;
  716. LPSTR valueToMatch = NULL;
  717. ANSI_STRING aString;
  718. UNICODE_STRING uString;
  719. //
  720. // First look at lineToMatch to see what we are looking
  721. // for. This is the text up to the first =, or all of it if there
  722. // is no =. Once found, we convert it to ANSI for comparisons.
  723. //
  724. endOfValue = wcschr(lineToMatch, L'=');
  725. if (endOfValue == NULL) {
  726. endOfValue = lineToMatch + wcslen(lineToMatch);
  727. }
  728. valueLength = (DWORD)(endOfValue - lineToMatch);
  729. //
  730. // Copy the sectionName to ANSI for comparisons.
  731. //
  732. uString.Buffer = lineToMatch;
  733. uString.Length = (USHORT)(valueLength*sizeof(WCHAR));
  734. ansiValueLength = RtlUnicodeStringToAnsiSize(&uString); // includes final '\0'
  735. valueToMatch = BinlAllocateMemory(ansiValueLength);
  736. if (valueToMatch == NULL) {
  737. return FALSE;
  738. }
  739. aString.Buffer = valueToMatch;
  740. aString.MaximumLength = (USHORT)ansiValueLength;
  741. RtlUnicodeStringToAnsiString(
  742. &aString,
  743. &uString,
  744. FALSE);
  745. --ansiValueLength; // remove final '\0' from the count
  746. //
  747. // now scan each line of SectionStart, until we find the beginning
  748. // of another section, a \0, or the matching line.
  749. //
  750. curSection = SectionStart;
  751. while (*curSection != '\0') {
  752. //
  753. // At this point in the while, curSection points to the beginning
  754. // of a line. Save the start of the current line.
  755. //
  756. curLine = curSection;
  757. //
  758. // First find the first non-blank character.
  759. //
  760. while ((*curSection != '\0') && (*curSection == ' ')) {
  761. ++curSection;
  762. }
  763. //
  764. // If we hit \0, we didn't find it.
  765. //
  766. if (*curSection == '\0') {
  767. break;
  768. }
  769. //
  770. // If we hit a line starting with [, we didn't find it.
  771. //
  772. if (*curSection == '[') {
  773. break;
  774. }
  775. //
  776. // If we hit a line starting with what we expect, followed
  777. // by an =, \0, or a blank, we found it.
  778. //
  779. if (strncmp(curSection, valueToMatch, ansiValueLength) == 0) {
  780. if ((curSection[ansiValueLength] == '=') ||
  781. (curSection[ansiValueLength] == '\0') ||
  782. (curSection[ansiValueLength] == ' ')) {
  783. *existingLine = curLine;
  784. endOfLine = strchr(curLine, '\n');
  785. if (endOfLine == NULL) {
  786. *existingLineLen = strlen(curLine);
  787. } else {
  788. *existingLineLen = (DWORD)(endOfLine + 1 - curLine);
  789. }
  790. foundLine = TRUE;
  791. break;
  792. }
  793. }
  794. //
  795. // Now scan to the start of the next line, defined as the
  796. // character after a \n.
  797. //
  798. while ((*curSection != L'\0') && (*curSection != L'\n')) {
  799. ++curSection;
  800. }
  801. if (*curSection == L'\0') {
  802. break;
  803. }
  804. ++curSection; // skip past the '\n'
  805. }
  806. if (valueToMatch != NULL) {
  807. BinlFreeMemory(valueToMatch);
  808. }
  809. return foundLine;
  810. }
  811. VOID
  812. ProcessUniqueUdb(
  813. LPSTR *sifFilePtr,
  814. DWORD sifFileLen,
  815. LPWSTR UniqueUdbPath,
  816. LPWSTR UniqueUdbId
  817. )
  818. /*++
  819. Routine Description:
  820. Overlays data from a unique.udb file based on the tag specified. The
  821. file to overlay on is in memory at *pszString. *pszString is reallocated
  822. if necessary.
  823. Arguments:
  824. sifFile - The file to overlay data on.
  825. sifFileLen - The current size of the data at *pszString (including final NULL).
  826. UniqueUdbPath - The path to the unique.udb file.
  827. UniqueUdbId - The ID in unique.udb to use.
  828. Return Value:
  829. None.
  830. --*/
  831. {
  832. PWCHAR TmpBuffer = NULL;
  833. DWORD len, sifFileAlloc, lineLen;
  834. LONG sizeToAdd;
  835. LPSTR sifFile = *sifFilePtr;
  836. PWCHAR sectionList = NULL;
  837. PWCHAR sectionLoc, sectionCur;
  838. PWCHAR sectionName = NULL;
  839. PCHAR ansiRealSectionName = NULL;
  840. PCHAR sectionStart;
  841. PWCHAR profileSectionCur;
  842. PWCHAR realSectionName;
  843. PCHAR existingLine;
  844. DWORD existingLineLen;
  845. DWORD lenRealSectionName;
  846. PCHAR insertionPoint;
  847. ANSI_STRING aString;
  848. UNICODE_STRING uString;
  849. #define TMP_BUFFER_SIZE 2048
  850. TraceFunc("ProcessUniqueUdb( )\n");
  851. TmpBuffer = BinlAllocateMemory(TMP_BUFFER_SIZE * sizeof(WCHAR));
  852. if (TmpBuffer == NULL) {
  853. return;
  854. }
  855. //
  856. // See if the ID appears in the [UniqueIds] section of the unique.udb file.
  857. //
  858. TmpBuffer[0] = L'\0';
  859. GetPrivateProfileString( L"UniqueIds", // section name
  860. UniqueUdbId, // line name
  861. L"", // default
  862. TmpBuffer,
  863. TMP_BUFFER_SIZE,
  864. UniqueUdbPath );
  865. if (TmpBuffer[0] == L'\0') {
  866. return;
  867. }
  868. //
  869. // sifFileAlloc is the size allocated for sifFile, whereas
  870. // sifFileLen is the amount actually used. They should only
  871. // be different while we are actively shuffling things
  872. // around.
  873. //
  874. sifFileAlloc = sifFileLen;
  875. //
  876. // Save the tmpbuffer result.
  877. //
  878. len = wcslen(TmpBuffer) + 1;
  879. sectionList = BinlAllocateMemory(len * sizeof(WCHAR));
  880. if (sectionList == NULL) {
  881. return;
  882. }
  883. wcscpy(sectionList, TmpBuffer);
  884. //
  885. // Now for each section identified in sectionList, scan for
  886. // the section to overlay.
  887. //
  888. sectionLoc = sectionList;
  889. while (TRUE) {
  890. //
  891. // First skip leading blanks
  892. //
  893. while (*sectionLoc && !iswalnum(*sectionLoc)) {
  894. //
  895. // Make sure we are not at a comment.
  896. //
  897. if (*sectionLoc == L';') {
  898. goto Cleanup;
  899. }
  900. ++sectionLoc;
  901. }
  902. if (!*sectionLoc) {
  903. goto Cleanup;
  904. }
  905. //
  906. // Now save sectionCur as the current section name
  907. // and skip to the end of it. Section names can be
  908. // any alphanumeric character, '.', or '_'.
  909. //
  910. sectionCur = sectionLoc;
  911. while((iswalnum(*sectionLoc)) ||
  912. (*sectionLoc == '.') ||
  913. (*sectionLoc == '_')) {
  914. ++sectionLoc;
  915. }
  916. //
  917. // Construct the new section name to look for. This will
  918. // be [UNIQUEUDBID:RealSectionName].
  919. //
  920. len = wcslen(UniqueUdbId) + (sectionLoc - sectionCur) + 2; // one for :, one for NULL
  921. sectionName = BinlAllocateMemory(len * sizeof(WCHAR));
  922. if (sectionName == NULL) {
  923. goto Cleanup;
  924. }
  925. wcscpy(sectionName, UniqueUdbId);
  926. wcscat(sectionName, L":");
  927. len = wcslen(sectionName);
  928. realSectionName = sectionName + len;
  929. memcpy(realSectionName, sectionCur, (sectionLoc - sectionCur) * sizeof(WCHAR));
  930. realSectionName[sectionLoc - sectionCur] = L'\0';
  931. //
  932. // Copy the sectionName to ANSI for comparisons.
  933. //
  934. uString.Buffer = realSectionName;
  935. uString.Length = (USHORT)(wcslen(realSectionName)*sizeof(WCHAR));
  936. lenRealSectionName = RtlUnicodeStringToAnsiSize(&uString); // includes final '\0'
  937. ansiRealSectionName = BinlAllocateMemory(lenRealSectionName);
  938. if (ansiRealSectionName == NULL) {
  939. goto Cleanup;
  940. }
  941. aString.Buffer = ansiRealSectionName;
  942. aString.MaximumLength = (USHORT)lenRealSectionName;
  943. RtlUnicodeStringToAnsiString(
  944. &aString,
  945. &uString,
  946. FALSE);
  947. --lenRealSectionName; // remove final '\0' from count
  948. //
  949. // See if there is a section with that name.
  950. //
  951. TmpBuffer[0] = L'\0';
  952. GetPrivateProfileSection( sectionName,
  953. TmpBuffer,
  954. TMP_BUFFER_SIZE,
  955. UniqueUdbPath );
  956. if (TmpBuffer[0] == L'\0') {
  957. continue;
  958. }
  959. //
  960. // Got the contents of the section, now process it.
  961. //
  962. sectionStart = FindSection(ansiRealSectionName, sifFile);
  963. sizeToAdd = 0; // amount we need to extend the buffer by.
  964. if (sectionStart == NULL) {
  965. //
  966. // No section, so need to add room for it.
  967. //
  968. // We put a CR-LF combo, then the section name in [], then
  969. // another CR-LF.
  970. //
  971. // lenRealSectionName already contains the '\0'
  972. //
  973. sizeToAdd = lenRealSectionName + 6;
  974. }
  975. //
  976. // Now scan through the entries in the profile section.
  977. //
  978. profileSectionCur = TmpBuffer;
  979. while (*profileSectionCur != L'\0') {
  980. uString.Buffer = profileSectionCur;
  981. uString.Length = (USHORT)(wcslen(profileSectionCur) * sizeof(WCHAR));
  982. //
  983. // Figure out how long profileSectionCur will be as an
  984. // ANSI string (may have DBCS data in it).
  985. //
  986. lineLen = RtlUnicodeStringToAnsiSize(&uString); // includes \0 termination
  987. --lineLen; // remove the \0 from the count
  988. //
  989. // If there is no existing section we have to add it;
  990. // if not, see if there is a line for this already.
  991. //
  992. if (sectionStart == NULL) {
  993. sizeToAdd += lineLen + 2; // +2 is for CR-LF
  994. } else {
  995. if (FindLineInSection(sectionStart,
  996. profileSectionCur,
  997. &existingLine,
  998. &existingLineLen)) {
  999. //
  1000. // Need to remove current line.
  1001. //
  1002. memmove(existingLine, existingLine + existingLineLen,
  1003. sifFileLen - ((existingLine - sifFile) + existingLineLen));
  1004. sizeToAdd += lineLen + 2 - existingLineLen;
  1005. sifFileLen -= existingLineLen;
  1006. } else {
  1007. sizeToAdd += lineLen + 2;
  1008. }
  1009. }
  1010. profileSectionCur += wcslen(profileSectionCur) + 1;
  1011. }
  1012. //
  1013. // Now we need to reallocate the buffer if needed.
  1014. //
  1015. if (sizeToAdd > 0) {
  1016. //
  1017. // No. Make some space
  1018. //
  1019. LPSTR pszNewString;
  1020. pszNewString = BinlAllocateMemory( sifFileAlloc + sizeToAdd );
  1021. if ( !pszNewString )
  1022. {
  1023. return; // abort the rest
  1024. }
  1025. MoveMemory( pszNewString, sifFile, sifFileLen);
  1026. BinlFreeMemory( sifFile );
  1027. //
  1028. // Adjust sectionStart to be within the new buffer.
  1029. //
  1030. if (sectionStart != NULL) {
  1031. sectionStart = pszNewString + (sectionStart - sifFile);
  1032. }
  1033. sifFile = pszNewString;
  1034. *sifFilePtr = pszNewString;
  1035. sifFileAlloc += sizeToAdd;
  1036. }
  1037. //
  1038. // Add the section header if necessary.
  1039. //
  1040. if (sectionStart == NULL) {
  1041. strcpy(sifFile + sifFileLen - 1, "\r\n[");
  1042. sifFileLen += 3;
  1043. strcpy(sifFile + sifFileLen - 1, ansiRealSectionName);
  1044. sifFileLen += lenRealSectionName;
  1045. strcpy(sifFile + sifFileLen - 1, "]\r\n");
  1046. sifFileLen += 3;
  1047. sectionStart = sifFile + sifFileLen - 1;
  1048. }
  1049. //
  1050. // Now add the items from the profile section. We know that
  1051. // they do not exist in the file and that we have reallocated
  1052. // the file buffer to be large enough.
  1053. //
  1054. profileSectionCur = TmpBuffer;
  1055. insertionPoint = sectionStart;
  1056. while (*profileSectionCur != L'\0') {
  1057. uString.Buffer = profileSectionCur;
  1058. uString.Length = (USHORT)(wcslen(profileSectionCur)*sizeof(WCHAR));
  1059. lineLen = RtlUnicodeStringToAnsiSize(&uString); // includes final '\0'
  1060. --lineLen; // remove final '\0' from count
  1061. //
  1062. // move anything we need to down and insert the new line.
  1063. //
  1064. memmove(insertionPoint + lineLen + 2, insertionPoint, sifFileLen - (insertionPoint - sifFile));
  1065. aString.Buffer = insertionPoint;
  1066. aString.MaximumLength = (USHORT)(lineLen+1);
  1067. RtlUnicodeStringToAnsiString(
  1068. &aString,
  1069. &uString,
  1070. FALSE);
  1071. memcpy(insertionPoint + lineLen, "\r\n", 2);
  1072. sifFileLen += lineLen + 2;
  1073. insertionPoint += lineLen + 2;
  1074. profileSectionCur += wcslen(profileSectionCur) + 1;
  1075. }
  1076. }
  1077. Cleanup:
  1078. if (sectionList != NULL) {
  1079. BinlFreeMemory(sectionList);
  1080. }
  1081. if (sectionName != NULL) {
  1082. BinlFreeMemory(sectionName);
  1083. }
  1084. if (ansiRealSectionName != NULL) {
  1085. BinlFreeMemory(ansiRealSectionName);
  1086. }
  1087. if (TmpBuffer != NULL) {
  1088. BinlFreeMemory(TmpBuffer);
  1089. }
  1090. ASSERT (sifFileLen <= sifFileAlloc);
  1091. }
  1092. //
  1093. // OscFindVariableA( )
  1094. //
  1095. LPSTR
  1096. OscFindVariableA(
  1097. PCLIENT_STATE clientState,
  1098. LPSTR variableName ) // variable name are always ASCII until OSChooser
  1099. // can handle Unicode.
  1100. {
  1101. ULONG i;
  1102. static CHAR szNullStringA[1] = { '\0' };
  1103. LPSTR overrideValue;
  1104. //
  1105. // First check to see if this a query we are supposed to override.
  1106. //
  1107. if (strcmp(variableName, "SIF") == 0) {
  1108. overrideValue = OscFindVariableA(clientState, "FORCESIFFILE");
  1109. if ((overrideValue != NULL) && (strlen(overrideValue) != 0)) {
  1110. return overrideValue;
  1111. }
  1112. }
  1113. for( i = 0; i < clientState->nVariables; i++ )
  1114. {
  1115. if ( strcmp( clientState->Variables[i].pszToken, variableName ) == 0 )
  1116. {
  1117. if ( clientState->Variables[i].pszStringA == NULL )
  1118. {
  1119. DWORD dwLen;
  1120. ANSI_STRING aString;
  1121. UNICODE_STRING uString;
  1122. uString.Buffer = clientState->Variables[i].pszStringW;
  1123. uString.Length = (USHORT)( wcslen( clientState->Variables[i].pszStringW ) * sizeof(WCHAR) );
  1124. dwLen = RtlUnicodeStringToAnsiSize( &uString ); // includes NULL termination
  1125. clientState->Variables[i].pszStringA = BinlAllocateMemory( dwLen );
  1126. if ( !(clientState->Variables[i].pszStringA) )
  1127. break; // out of memory
  1128. aString.Buffer = clientState->Variables[i].pszStringA;
  1129. aString.MaximumLength = (USHORT)dwLen;
  1130. RtlUnicodeStringToAnsiString(
  1131. &aString,
  1132. &uString,
  1133. FALSE);
  1134. }
  1135. return clientState->Variables[i].pszStringA;
  1136. }
  1137. }
  1138. return szNullStringA;
  1139. }
  1140. //
  1141. // OscFindVariableW( )
  1142. //
  1143. LPWSTR
  1144. OscFindVariableW(
  1145. PCLIENT_STATE clientState,
  1146. LPSTR variableName ) // variable name are always ASCII until OSChooser
  1147. // can handle Unicode.
  1148. {
  1149. ULONG i;
  1150. static WCHAR szNullStringW[1] = { L'\0' };
  1151. LPWSTR overrideValue;
  1152. //
  1153. // First check to see if this a query we are supposed to override.
  1154. //
  1155. if (strcmp(variableName, "SIF") == 0) {
  1156. overrideValue = OscFindVariableW(clientState, "FORCESIFFILE");
  1157. if ((overrideValue != NULL) && (wcslen(overrideValue) != 0)) {
  1158. return overrideValue;
  1159. }
  1160. }
  1161. for( i = 0; i < clientState->nVariables; i++ )
  1162. {
  1163. if ( strcmp( clientState->Variables[i].pszToken, variableName ) == 0 )
  1164. {
  1165. if ( clientState->Variables[i].pszStringW == NULL )
  1166. {
  1167. DWORD dwLen = _mbslen( clientState->Variables[i].pszStringA ) + 1;
  1168. ANSI_STRING aString;
  1169. UNICODE_STRING uString;
  1170. clientState->Variables[i].pszStringW = BinlAllocateMemory( dwLen * sizeof(WCHAR) );
  1171. if ( !(clientState->Variables[i].pszStringW) )
  1172. break; // out of memory
  1173. uString.Buffer = clientState->Variables[i].pszStringW;
  1174. uString.MaximumLength = (USHORT)(dwLen * sizeof(WCHAR));
  1175. aString.Buffer = clientState->Variables[i].pszStringA;
  1176. aString.Length = (USHORT)strlen( clientState->Variables[i].pszStringA );
  1177. RtlAnsiStringToUnicodeString(
  1178. &uString,
  1179. &aString,
  1180. FALSE);
  1181. }
  1182. return clientState->Variables[i].pszStringW;
  1183. }
  1184. }
  1185. return szNullStringW;
  1186. }
  1187. //
  1188. // OscCheckVariableLength( )
  1189. //
  1190. BOOLEAN
  1191. OscCheckVariableLength(
  1192. PCLIENT_STATE clientState,
  1193. LPSTR variableName,
  1194. ULONG variableLength )
  1195. {
  1196. ULONG i;
  1197. for (i = 0; i < OSC_VARIABLE_MAXIMUM_COUNT; i++) {
  1198. if (strcmp(OscMaximums[i].VariableName, variableName) == 0) {
  1199. if (variableLength > OscMaximums[i].MaximumLength) {
  1200. BinlPrintDbg((DEBUG_OSC_ERROR, "Variable %s was %d bytes, only %d allowed\n",
  1201. variableName,
  1202. variableLength,
  1203. OscMaximums[i].MaximumLength));
  1204. OscAddVariableA( clientState, "SUBERROR", variableName );
  1205. return FALSE;
  1206. } else {
  1207. return TRUE;
  1208. }
  1209. }
  1210. }
  1211. //
  1212. // If we don't find it in our list of maximums, it is OK.
  1213. //
  1214. return TRUE;
  1215. }
  1216. //
  1217. // OscAddVariableA( )
  1218. //
  1219. DWORD
  1220. OscAddVariableA(
  1221. PCLIENT_STATE clientState,
  1222. LPSTR variableName, // variable name are always ASCII until OSChooser
  1223. // can handle Unicode.
  1224. LPSTR variableValue )
  1225. {
  1226. ULONG i;
  1227. if ( variableValue == NULL )
  1228. return E_POINTER; // no value to add... abort
  1229. if (!OscCheckVariableLength(clientState, variableName, strlen(variableValue))) {
  1230. return E_INVALIDARG;
  1231. }
  1232. for( i = 0; i < clientState->nVariables; i++ )
  1233. {
  1234. if ( strcmp( clientState->Variables[i].pszToken, variableName ) == 0 )
  1235. {
  1236. ULONG l = strlen( variableValue );
  1237. if ( clientState->Variables[i].pszStringW != NULL )
  1238. {
  1239. BinlFreeMemory( clientState->Variables[i].pszStringW );
  1240. clientState->Variables[i].pszStringW = NULL;
  1241. }
  1242. if ( clientState->Variables[i].pszStringA != NULL )
  1243. { // found a previous one
  1244. // Don't replace values with ""
  1245. if ( variableValue[0] == '\0' ) {
  1246. break;
  1247. } else {
  1248. // replace it with the new one
  1249. if ( l <= strlen( clientState->Variables[i].pszStringA ) )
  1250. {
  1251. strcpy( clientState->Variables[i].pszStringA, variableValue );
  1252. }
  1253. else
  1254. {
  1255. // need more space, delete the old
  1256. BinlFreeMemory( clientState->Variables[i].pszStringA );
  1257. clientState->Variables[i].pszStringA = NULL;
  1258. }
  1259. }
  1260. }
  1261. break;
  1262. }
  1263. }
  1264. //
  1265. // Limit the number of variables we can have. Everything else is ignored.
  1266. //
  1267. if ( i == MAX_VARIABLES ) {
  1268. return E_OUTOFMEMORY;
  1269. }
  1270. //
  1271. // Adding a new one
  1272. //
  1273. if ( clientState->nVariables == i )
  1274. {
  1275. clientState->Variables[i].pszToken = BinlStrDupA( variableName );
  1276. if (clientState->Variables[i].pszToken == NULL) {
  1277. return E_OUTOFMEMORY;
  1278. }
  1279. clientState->nVariables++;
  1280. }
  1281. //
  1282. // If this is a new one or a new Value for an existing variable
  1283. // that does not fit in the old values space, create a dup of the
  1284. // value.
  1285. //
  1286. if ( clientState->Variables[i].pszStringA == NULL )
  1287. {
  1288. BinlAssert( variableValue != NULL );
  1289. clientState->Variables[i].pszStringA = BinlStrDupA( variableValue );
  1290. if (clientState->Variables[i].pszStringA == NULL) {
  1291. if ((i + 1) == clientState->nVariables) {
  1292. clientState->nVariables--;
  1293. BinlFreeMemory(clientState->Variables[i].pszToken);
  1294. }
  1295. return E_OUTOFMEMORY;
  1296. }
  1297. //
  1298. // The "OPTIONS" variable can have a lot of stuff in it and will
  1299. // blow up the BinlPrint(). Just avoid the whole mess by not
  1300. // printing it
  1301. //
  1302. if ( strcmp( clientState->Variables[i].pszToken, "OPTIONS" ) != 0 ) {
  1303. BinlPrintDbg((DEBUG_OSC, "Add Var:'%s' = '%s'\n",
  1304. clientState->Variables[i].pszToken,
  1305. clientState->Variables[i].pszStringA ));
  1306. }
  1307. }
  1308. //
  1309. // it will be converted to UNICODE when OscFindVariableW( ) is called
  1310. //
  1311. return ERROR_SUCCESS;
  1312. }
  1313. //
  1314. // OscAddVariableW( )
  1315. //
  1316. DWORD
  1317. OscAddVariableW(
  1318. PCLIENT_STATE clientState,
  1319. LPSTR variableName, // variable name are always ASCII until OSChooser
  1320. // can handle Unicode.
  1321. LPWSTR variableValue )
  1322. {
  1323. ULONG i;
  1324. if ( variableValue == NULL )
  1325. return E_POINTER; // no value to add... abort
  1326. if (!OscCheckVariableLength(clientState, variableName, wcslen(variableValue))) {
  1327. return E_INVALIDARG;
  1328. }
  1329. for( i = 0; i < clientState->nVariables; i++ )
  1330. {
  1331. if ( strcmp( clientState->Variables[i].pszToken, variableName ) == 0 )
  1332. {
  1333. if ( clientState->Variables[i].pszStringA != NULL )
  1334. {
  1335. BinlFreeMemory( clientState->Variables[i].pszStringA );
  1336. clientState->Variables[i].pszStringA = NULL;
  1337. }
  1338. if ( clientState->Variables[i].pszStringW != NULL )
  1339. { // found a previous one
  1340. // Don't replace values with ""
  1341. if ( variableValue[0] == L'\0' ) {
  1342. break;
  1343. } else {
  1344. // replace it with the new one
  1345. ULONG Length = wcslen( variableValue );
  1346. if ( Length < wcslen( clientState->Variables[i].pszStringW ) )
  1347. {
  1348. wcscpy( clientState->Variables[i].pszStringW, variableValue );
  1349. }
  1350. else
  1351. {
  1352. // need more space, delete the old
  1353. BinlFreeMemory( clientState->Variables[i].pszStringW );
  1354. clientState->Variables[i].pszStringW = NULL;
  1355. }
  1356. }
  1357. }
  1358. break;
  1359. }
  1360. }
  1361. //
  1362. // Limit the number of variables we can have. Everything else is ignored.
  1363. //
  1364. if ( i == MAX_VARIABLES ) {
  1365. return E_OUTOFMEMORY; // out of space
  1366. }
  1367. //
  1368. // Adding a new one
  1369. //
  1370. if ( clientState->nVariables == i )
  1371. {
  1372. clientState->Variables[i].pszToken = BinlStrDupA( variableName );
  1373. if (clientState->Variables[i].pszToken == NULL) {
  1374. return E_OUTOFMEMORY;
  1375. }
  1376. clientState->nVariables++;
  1377. }
  1378. //
  1379. // If this is a new one or a new Value for an existing variable
  1380. // that does not fit in the old values space, create a dup of the
  1381. // value.
  1382. //
  1383. if ( clientState->Variables[i].pszStringW == NULL )
  1384. {
  1385. BinlAssert( variableValue != NULL );
  1386. clientState->Variables[i].pszStringW = BinlStrDupW( variableValue);
  1387. if (clientState->Variables[i].pszStringW == NULL) {
  1388. if ((i + 1) == clientState->nVariables) {
  1389. clientState->nVariables--;
  1390. BinlFreeMemory(clientState->Variables[i].pszToken);
  1391. }
  1392. return E_OUTOFMEMORY;
  1393. }
  1394. BinlPrintDbg((DEBUG_OSC, "Add Var:'%s' = '%ws'\n",
  1395. clientState->Variables[i].pszToken,
  1396. clientState->Variables[i].pszStringW ));
  1397. }
  1398. //
  1399. // it will be converted to ASCII when OscFindVariableA( ) is called
  1400. //
  1401. return ERROR_SUCCESS;
  1402. }
  1403. //
  1404. // OscResetVariable( )
  1405. //
  1406. VOID
  1407. OscResetVariable(
  1408. PCLIENT_STATE clientState,
  1409. LPSTR variableName
  1410. )
  1411. {
  1412. ULONG i;
  1413. BOOLEAN found = FALSE;
  1414. for( i = 0; i < clientState->nVariables; i++ ) {
  1415. if ( strcmp( clientState->Variables[i].pszToken, variableName ) == 0 ) {
  1416. if ( clientState->Variables[i].pszStringA != NULL ) {
  1417. BinlFreeMemory( clientState->Variables[i].pszStringA );
  1418. clientState->Variables[i].pszStringA = NULL;
  1419. }
  1420. if ( clientState->Variables[i].pszStringW != NULL ) { // found a previous one
  1421. BinlFreeMemory( clientState->Variables[i].pszStringW );
  1422. clientState->Variables[i].pszStringW = NULL;
  1423. }
  1424. BinlPrintDbg((DEBUG_OSC, "Deleted Var:'%s'\n",
  1425. clientState->Variables[i].pszToken ));
  1426. BinlFreeMemory( clientState->Variables[i].pszToken );
  1427. found = TRUE;
  1428. break;
  1429. }
  1430. }
  1431. if (found) {
  1432. //
  1433. // move all existing ones up.
  1434. //
  1435. while (i < clientState->nVariables) {
  1436. clientState->Variables[i].pszToken = clientState->Variables[i+1].pszToken;
  1437. clientState->Variables[i].pszStringA = clientState->Variables[i+1].pszStringA;
  1438. clientState->Variables[i].pszStringW = clientState->Variables[i+1].pszStringW;
  1439. i++;
  1440. }
  1441. clientState->nVariables--;
  1442. }
  1443. return;
  1444. }