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.

6562 lines
166 KiB

  1. /*++
  2. Copyright (c) 1990 - 1995 Microsoft Corporation
  3. Module Name:
  4. util.c
  5. Abstract:
  6. This module provides all the utility functions for the Routing Layer and
  7. the local Print Providor
  8. Author:
  9. Dave Snipp (DaveSn) 15-Mar-1991
  10. Revision History:
  11. Felix Maxa (amaxa) 18-Jun-2000
  12. Added utility functions for cluster spoolers. Part of the DCR regarding
  13. installation of printer drivers on cluster spoolers
  14. Muhunthan Sivapragasam ( MuhuntS ) 5-June-1995
  15. Moved from printer.c:
  16. RegSetBinaryData
  17. RegSetString
  18. RegSetDWord
  19. Wrote:
  20. SameMultiSz
  21. RegGetValue
  22. Matthew A Felton ( MattFe ) 23-mar-1995
  23. DeleteAllFilesAndDirectory
  24. DeleteAllFilesInDirectory
  25. CreateDirectoryWithoutImpersonatingUser
  26. --*/
  27. #include <precomp.h>
  28. #pragma hdrstop
  29. #include <winddiui.h>
  30. #include <lm.h>
  31. #include <aclapi.h>
  32. #include <winsta.h>
  33. #include "clusspl.h"
  34. typedef LONG (WINAPI *pfnWinStationSendWindowMessage)(
  35. HANDLE hServer,
  36. ULONG sessionID,
  37. ULONG timeOut,
  38. ULONG hWnd,
  39. ULONG Msg,
  40. WPARAM wParam,
  41. LPARAM lParam,
  42. LONG *pResponse);
  43. extern BOOL (*pfnOpenPrinter)(LPTSTR, LPHANDLE, LPPRINTER_DEFAULTS);
  44. extern BOOL (*pfnClosePrinter)(HANDLE);
  45. extern LONG (*pfnDocumentProperties)(HWND, HANDLE, LPWSTR, PDEVMODE, PDEVMODE, DWORD);
  46. #define DEFAULT_MAX_TIMEOUT 300000 // 5 minute timeout.
  47. CRITICAL_SECTION SpoolerSection;
  48. PDBG_POINTERS gpDbgPointers = NULL;
  49. pfnWinStationSendWindowMessage pfWinStationSendWindowMessage = NULL;
  50. VOID
  51. RunForEachSpooler(
  52. HANDLE h,
  53. PFNSPOOLER_MAP pfnMap
  54. )
  55. {
  56. PINISPOOLER pIniSpooler;
  57. PINISPOOLER pIniNextSpooler;
  58. SplInSem();
  59. if( pLocalIniSpooler ){
  60. INCSPOOLERREF( pLocalIniSpooler );
  61. for( pIniSpooler = pLocalIniSpooler; pIniSpooler; pIniSpooler = pIniNextSpooler ){
  62. if( (*pfnMap)( h, pIniSpooler ) ){
  63. pIniNextSpooler = pIniSpooler->pIniNextSpooler;
  64. } else {
  65. pIniNextSpooler = NULL;
  66. }
  67. if( pIniNextSpooler ){
  68. INCSPOOLERREF( pIniNextSpooler );
  69. }
  70. DECSPOOLERREF( pIniSpooler );
  71. }
  72. }
  73. }
  74. VOID
  75. RunForEachPrinter(
  76. PINISPOOLER pIniSpooler,
  77. HANDLE h,
  78. PFNPRINTER_MAP pfnMap
  79. )
  80. {
  81. PINIPRINTER pIniPrinter;
  82. PINIPRINTER pIniNextPrinter;
  83. SplInSem();
  84. pIniPrinter = pIniSpooler->pIniPrinter;
  85. if( pIniPrinter ){
  86. INCPRINTERREF( pIniPrinter );
  87. for( ; pIniPrinter; pIniPrinter = pIniNextPrinter ){
  88. if( (*pfnMap)( h, pIniPrinter ) ){
  89. pIniNextPrinter = pIniPrinter->pNext;
  90. } else {
  91. pIniNextPrinter = NULL;
  92. }
  93. if( pIniNextPrinter ){
  94. INCPRINTERREF( pIniNextPrinter );
  95. }
  96. DECPRINTERREF( pIniPrinter );
  97. DeletePrinterCheck( pIniPrinter );
  98. }
  99. }
  100. }
  101. #if DBG
  102. HANDLE hcsSpoolerSection = NULL;
  103. VOID
  104. SplInSem(
  105. VOID
  106. )
  107. {
  108. if( hcsSpoolerSection ){
  109. SPLASSERT( gpDbgPointers->pfnInsideCritSec( hcsSpoolerSection ));
  110. } else {
  111. SPLASSERT( SpoolerSection.OwningThread == (HANDLE)(ULONG_PTR)(GetCurrentThreadId( )));
  112. }
  113. }
  114. VOID
  115. SplOutSem(
  116. VOID
  117. )
  118. {
  119. if( hcsSpoolerSection ){
  120. SPLASSERT( gpDbgPointers->pfnOutsideCritSec( hcsSpoolerSection ));
  121. } else {
  122. SPLASSERT( SpoolerSection.OwningThread != (HANDLE)((ULONG_PTR)GetCurrentThreadId( )));
  123. }
  124. }
  125. #endif // DBG
  126. VOID
  127. EnterSplSem(
  128. VOID
  129. )
  130. {
  131. #if DBG
  132. if( hcsSpoolerSection ){
  133. gpDbgPointers->pfnEnterCritSec( hcsSpoolerSection );
  134. } else {
  135. EnterCriticalSection( &SpoolerSection );
  136. }
  137. #else
  138. EnterCriticalSection( &SpoolerSection );
  139. #endif
  140. }
  141. VOID
  142. LeaveSplSem(
  143. VOID
  144. )
  145. {
  146. #if DBG
  147. if( hcsSpoolerSection ){
  148. gpDbgPointers->pfnLeaveCritSec( hcsSpoolerSection );
  149. } else {
  150. LeaveCriticalSection( &SpoolerSection );
  151. }
  152. #else
  153. LeaveCriticalSection( &SpoolerSection );
  154. #endif
  155. }
  156. PDEVMODE
  157. AllocDevMode(
  158. PDEVMODE pDevMode
  159. )
  160. {
  161. PDEVMODE pDevModeAlloc = NULL;
  162. DWORD Size;
  163. if (pDevMode) {
  164. Size = pDevMode->dmSize + pDevMode->dmDriverExtra;
  165. if(pDevModeAlloc = AllocSplMem(Size)) {
  166. memcpy(pDevModeAlloc, pDevMode, Size);
  167. }
  168. }
  169. return pDevModeAlloc;
  170. }
  171. BOOL
  172. FreeDevMode(
  173. PDEVMODE pDevMode
  174. )
  175. {
  176. if (pDevMode) {
  177. FreeSplMem((PVOID)pDevMode);
  178. return TRUE;
  179. } else {
  180. return FALSE;
  181. }
  182. }
  183. PINIENTRY
  184. FindName(
  185. PINIENTRY pIniKey,
  186. LPWSTR pName
  187. )
  188. {
  189. if (pName) {
  190. while (pIniKey) {
  191. if (!lstrcmpi(pIniKey->pName, pName)) {
  192. return pIniKey;
  193. }
  194. pIniKey=pIniKey->pNext;
  195. }
  196. }
  197. return FALSE;
  198. }
  199. BOOL
  200. FileExists(
  201. LPWSTR pFileName
  202. )
  203. {
  204. if( GetFileAttributes( pFileName ) == 0xffffffff ){
  205. return FALSE;
  206. }
  207. return TRUE;
  208. }
  209. BOOL
  210. DirectoryExists(
  211. LPWSTR pDirectoryName
  212. )
  213. {
  214. DWORD dwFileAttributes;
  215. dwFileAttributes = GetFileAttributes( pDirectoryName );
  216. if ( dwFileAttributes != 0xffffffff &&
  217. dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
  218. return TRUE;
  219. }
  220. return FALSE;
  221. }
  222. BOOL
  223. CheckSepFile(
  224. IN LPWSTR pFileName
  225. )
  226. {
  227. BOOL bRetval = FALSE;
  228. //
  229. // NULL or "" is OK:
  230. //
  231. if (!pFileName || !*pFileName)
  232. {
  233. bRetval = TRUE;
  234. }
  235. else
  236. {
  237. //
  238. // If the name is not NULL or "" then the name must be less
  239. // than MAX_PATH and exist.
  240. //
  241. if ((wcslen(pFileName) < MAX_PATH-1) && FileExists(pFileName))
  242. {
  243. bRetval = TRUE;
  244. }
  245. }
  246. return bRetval;
  247. }
  248. DWORD
  249. GetFullNameFromId(
  250. PINIPRINTER pIniPrinter,
  251. DWORD JobId,
  252. BOOL fJob,
  253. LPWSTR pFileName,
  254. BOOL Remote
  255. )
  256. {
  257. DWORD i;
  258. //
  259. // MAX_PATH - 9 is tha maximum number of chars that we want to store in pFileName since we
  260. // want to concatenate the SPL/SHD file
  261. // If GetPrinterDirectory fails i is 0.
  262. // The right way to fix this is that the caller of GetFullNameFromId chackes for the return value
  263. // which is not the case.
  264. //
  265. i = GetPrinterDirectory(pIniPrinter, Remote, pFileName, MAX_PATH-9, pIniPrinter->pIniSpooler);
  266. pFileName[i++]=L'\\';
  267. wsprintf(&pFileName[i], L"%05d.%ws", JobId, fJob ? L"SPL" : L"SHD");
  268. #ifdef PREVIOUS
  269. for (i = 5; i--;) {
  270. pFileName[i++] = (CHAR)((JobId % 10) + '0');
  271. JobId /= 10;
  272. }
  273. #endif
  274. while (pFileName[i++])
  275. ;
  276. return i-1;
  277. }
  278. DWORD
  279. GetPrinterDirectory(
  280. PINIPRINTER pIniPrinter, // Can be NULL
  281. BOOL Remote,
  282. LPWSTR pDir,
  283. DWORD MaxLength,
  284. PINISPOOLER pIniSpooler
  285. )
  286. {
  287. DWORD i=0;
  288. LPWSTR psz;
  289. if (Remote) {
  290. DBGMSG(DBG_ERROR, ("GetPrinterDirectory called remotely. Not currently supported."));
  291. return 0;
  292. }
  293. if ((pIniPrinter == NULL) || (pIniPrinter->pSpoolDir == NULL) ) {
  294. if (pIniSpooler->pDefaultSpoolDir == NULL) {
  295. //
  296. // No default directory, then create a default. For cluster spoolers,
  297. // the default directory is N:\Spool, where N is the shared drive letter
  298. //
  299. if( StrNCatBuff(pDir,
  300. MaxLength,
  301. pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ? pIniSpooler->pszClusResDriveLetter :
  302. pIniSpooler->pDir,
  303. L"\\",
  304. pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ? szClusterPrinterDir : szPrinterDir,
  305. NULL) != ERROR_SUCCESS ) {
  306. return 0;
  307. }
  308. pIniSpooler->pDefaultSpoolDir = AllocSplStr(pDir);
  309. } else {
  310. // Give Caller the Default
  311. wcscpy(pDir, pIniSpooler->pDefaultSpoolDir);
  312. }
  313. } else {
  314. // Have Per Printer Directory
  315. wcscpy (pDir, pIniPrinter->pSpoolDir);
  316. }
  317. return (wcslen(pDir));
  318. }
  319. DWORD
  320. GetDriverDirectory(
  321. LPWSTR pDir,
  322. DWORD MaxLength,
  323. PINIENVIRONMENT pIniEnvironment,
  324. LPWSTR lpRemotePath,
  325. PINISPOOLER pIniSpooler
  326. )
  327. {
  328. LPWSTR psz;
  329. if (lpRemotePath) {
  330. if( StrNCatBuff(pDir,
  331. MaxLength,
  332. lpRemotePath,
  333. L"\\",
  334. pIniSpooler->pszDriversShare,
  335. L"\\",
  336. pIniEnvironment->pDirectory,
  337. NULL) != ERROR_SUCCESS ) {
  338. return 0;
  339. }
  340. } else {
  341. if( StrNCatBuff( pDir,
  342. MaxLength,
  343. pIniSpooler->pDir,
  344. L"\\",
  345. szDriverDir,
  346. L"\\",
  347. pIniEnvironment->pDirectory,
  348. NULL) != ERROR_SUCCESS ) {
  349. return 0;
  350. }
  351. }
  352. return wcslen(pDir);
  353. }
  354. DWORD
  355. GetProcessorDirectory(
  356. LPWSTR *pDir,
  357. LPWSTR pEnvironment,
  358. PINISPOOLER pIniSpooler
  359. )
  360. {
  361. return StrCatAlloc(pDir,
  362. pIniSpooler->pDir,
  363. L"\\",
  364. szPrintProcDir,
  365. L"\\",
  366. pEnvironment,
  367. NULL);
  368. }
  369. PINIENTRY
  370. FindIniKey(
  371. PINIENTRY pIniEntry,
  372. LPWSTR pName
  373. )
  374. {
  375. if ( pName == NULL ) {
  376. return NULL;
  377. }
  378. SplInSem();
  379. while ( pIniEntry && lstrcmpi( pName, pIniEntry->pName ))
  380. pIniEntry = pIniEntry->pNext;
  381. return pIniEntry;
  382. }
  383. BOOL
  384. CreateCompleteDirectory(
  385. LPWSTR pDir
  386. )
  387. {
  388. LPWSTR pBackSlash=pDir;
  389. do {
  390. pBackSlash = wcschr( pBackSlash, L'\\' );
  391. if ( pBackSlash != NULL )
  392. *pBackSlash = 0;
  393. CreateDirectory(pDir, NULL);
  394. if ( pBackSlash )
  395. *pBackSlash++=L'\\';
  396. } while ( pBackSlash );
  397. // BUBUG Always returns TRUE
  398. return TRUE;
  399. }
  400. LPCWSTR
  401. FindFileName(
  402. LPCWSTR pPathName
  403. )
  404. /*++
  405. Routine Description:
  406. Retrieve the filename portion of a path.
  407. This will can the input string until it finds the last backslash,
  408. then return the portion of the string immediately following it.
  409. If the string terminates with a backslash, then NULL is returned.
  410. Note: this can return illegal file names; no validation is done.
  411. Arguments:
  412. pPathName - Path name to parse.
  413. Return Value:
  414. Last portion of file name or NULL if none available.
  415. --*/
  416. {
  417. LPCWSTR pSlash;
  418. LPCWSTR pTemp;
  419. if( !pPathName ){
  420. return NULL;
  421. }
  422. pTemp = pPathName;
  423. while( pSlash = wcschr( pTemp, L'\\' )) {
  424. pTemp = pSlash+1;
  425. }
  426. if( !*pTemp ){
  427. return NULL;
  428. }
  429. return pTemp;
  430. }
  431. LPWSTR
  432. GetFileName(
  433. LPWSTR pPathName
  434. )
  435. {
  436. LPCWSTR pFileName;
  437. pFileName = FindFileName( pPathName );
  438. if (pFileName) {
  439. return( AllocSplStr( pFileName ) );
  440. } else {
  441. return(NULL);
  442. }
  443. }
  444. VOID
  445. CreatePrintProcDirectory(
  446. LPWSTR pEnvironment,
  447. PINISPOOLER pIniSpooler
  448. )
  449. {
  450. DWORD cb;
  451. LPWSTR pEnd;
  452. LPWSTR pPathName;
  453. cb = wcslen(pIniSpooler->pDir)*sizeof(WCHAR) +
  454. wcslen(pEnvironment)*sizeof(WCHAR) +
  455. wcslen(szPrintProcDir)*sizeof(WCHAR) +
  456. 4*sizeof(WCHAR);
  457. if (pPathName=AllocSplMem(cb)) {
  458. wcscpy(pPathName, pIniSpooler->pDir);
  459. pEnd=pPathName+wcslen(pPathName);
  460. if( CreateDirectory(pPathName, NULL) ||
  461. ( GetLastError() == ERROR_ALREADY_EXISTS )) {
  462. wcscpy(pEnd++, L"\\");
  463. wcscpy(pEnd, szPrintProcDir);
  464. if( CreateDirectory(pPathName, NULL) ||
  465. ( GetLastError() == ERROR_ALREADY_EXISTS )) {
  466. pEnd+=wcslen(pEnd);
  467. wcscpy(pEnd++, L"\\");
  468. wcscpy(pEnd, pEnvironment);
  469. if (CreateDirectory(pPathName, NULL) ||
  470. (GetLastError() == ERROR_ALREADY_EXISTS)) {
  471. pEnd+=wcslen(pEnd);
  472. }
  473. }
  474. }
  475. FreeSplMem(pPathName);
  476. }
  477. }
  478. BOOL
  479. RemoveFromList(
  480. PINIENTRY *ppIniHead,
  481. PINIENTRY pIniEntry
  482. )
  483. {
  484. while (*ppIniHead && *ppIniHead != pIniEntry) {
  485. ppIniHead = &(*ppIniHead)->pNext;
  486. }
  487. if (*ppIniHead)
  488. *ppIniHead = (*ppIniHead)->pNext;
  489. return(TRUE);
  490. }
  491. PKEYDATA
  492. CreateTokenList(
  493. LPWSTR pKeyData
  494. )
  495. {
  496. DWORD cTokens;
  497. DWORD cb;
  498. PKEYDATA pResult;
  499. LPWSTR pDest;
  500. LPWSTR psz = pKeyData;
  501. LPWSTR *ppToken;
  502. if (!psz || !*psz)
  503. return NULL;
  504. cTokens=1;
  505. // Scan through the string looking for commas,
  506. // ensuring that each is followed by a non-NULL character:
  507. while ((psz = wcschr(psz, L',')) && psz[1]) {
  508. cTokens++;
  509. psz++;
  510. }
  511. cb = sizeof(KEYDATA) + (cTokens-1) * sizeof(LPWSTR) +
  512. wcslen(pKeyData)*sizeof(WCHAR) + sizeof(WCHAR);
  513. if (!(pResult = (PKEYDATA)AllocSplMem(cb)))
  514. return NULL;
  515. // Initialise pDest to point beyond the token pointers:
  516. pDest = (LPWSTR)((LPBYTE)pResult + sizeof(KEYDATA) +
  517. (cTokens-1) * sizeof(LPWSTR));
  518. // Then copy the key data buffer there:
  519. wcscpy(pDest, pKeyData);
  520. ppToken = pResult->pTokens;
  521. psz = pDest;
  522. do {
  523. *ppToken++ = psz;
  524. if ( psz = wcschr(psz, L',') )
  525. *psz++ = L'\0';
  526. } while (psz);
  527. pResult->cTokens = cTokens;
  528. return( pResult );
  529. }
  530. VOID
  531. FreePortTokenList(
  532. PKEYDATA pKeyData
  533. )
  534. {
  535. PINIPORT pIniPort;
  536. DWORD i;
  537. if ( pKeyData ) {
  538. if ( pKeyData->bFixPortRef ) {
  539. for ( i = 0 ; i < pKeyData->cTokens ; ++i ) {
  540. pIniPort = (PINIPORT)pKeyData->pTokens[i];
  541. DECPORTREF(pIniPort);
  542. }
  543. }
  544. FreeSplMem(pKeyData);
  545. }
  546. }
  547. VOID
  548. GetPrinterPorts(
  549. PINIPRINTER pIniPrinter,
  550. LPWSTR pszPorts,
  551. DWORD *pcbNeeded
  552. )
  553. {
  554. PINIPORT pIniPort;
  555. BOOL Comma;
  556. DWORD i;
  557. DWORD cbNeeded = 0;
  558. SPLASSERT(pcbNeeded);
  559. // Determine required size
  560. Comma = FALSE;
  561. for ( i = 0 ; i < pIniPrinter->cPorts ; ++i ) {
  562. pIniPort = pIniPrinter->ppIniPorts[i];
  563. if ( pIniPort->Status & PP_FILE )
  564. continue;
  565. if ( Comma )
  566. cbNeeded += wcslen(szComma)*sizeof(WCHAR);
  567. cbNeeded += wcslen(pIniPort->pName)*sizeof(WCHAR);
  568. Comma = TRUE;
  569. }
  570. //
  571. // Add in size of NULL
  572. //
  573. cbNeeded += sizeof(WCHAR);
  574. if (pszPorts && cbNeeded <= *pcbNeeded) {
  575. //
  576. // If we are given a buffer & buffer is big enough, then fill it
  577. //
  578. Comma = FALSE;
  579. for ( i = 0 ; i < pIniPrinter->cPorts ; ++i ) {
  580. pIniPort = pIniPrinter->ppIniPorts[i];
  581. if ( pIniPort->Status & PP_FILE )
  582. continue;
  583. if ( Comma ) {
  584. wcscat(pszPorts, szComma);
  585. wcscat(pszPorts, pIniPort->pName);
  586. } else {
  587. wcscpy(pszPorts, pIniPort->pName);
  588. }
  589. Comma = TRUE;
  590. }
  591. }
  592. *pcbNeeded = cbNeeded;
  593. }
  594. BOOL
  595. MyName(
  596. LPWSTR pName,
  597. PINISPOOLER pIniSpooler
  598. )
  599. {
  600. EnterSplSem();
  601. if (CheckMyName(pName, pIniSpooler))
  602. {
  603. LeaveSplSem();
  604. return TRUE;
  605. }
  606. //
  607. // Only refresh machine names if pName is an IP or DNS address
  608. //
  609. if (pIniSpooler == pLocalIniSpooler &&
  610. wcschr(pName, L'.') &&
  611. RefreshMachineNamesCache() &&
  612. CheckMyName(pName, pIniSpooler))
  613. {
  614. LeaveSplSem();
  615. return TRUE;
  616. }
  617. SetLastError(ERROR_INVALID_NAME);
  618. LeaveSplSem();
  619. return FALSE;
  620. }
  621. BOOL
  622. RefreshMachineNamesCache(
  623. )
  624. {
  625. PWSTR *ppszOtherNames;
  626. DWORD cOtherNames;
  627. SplInSem();
  628. //
  629. // Get other machine names first. Only if it succeeds do we replace the cache.
  630. //
  631. if (!BuildOtherNamesFromMachineName(&ppszOtherNames, &cOtherNames))
  632. return FALSE;
  633. FreeOtherNames(&pLocalIniSpooler->ppszOtherNames, &pLocalIniSpooler->cOtherNames);
  634. pLocalIniSpooler->ppszOtherNames = ppszOtherNames;
  635. pLocalIniSpooler->cOtherNames = cOtherNames;
  636. return TRUE;
  637. }
  638. BOOL
  639. CheckMyName(
  640. LPWSTR pName,
  641. PINISPOOLER pIniSpooler
  642. )
  643. {
  644. DWORD dwIndex = 0;
  645. if (!pName || !*pName)
  646. return TRUE;
  647. if (*pName == L'\\' && *(pName+1) == L'\\') {
  648. if (!lstrcmpi(pName, pIniSpooler->pMachineName))
  649. return TRUE;
  650. while (dwIndex < pIniSpooler->cOtherNames) {
  651. if (!lstrcmpi(pName+2, pIniSpooler->ppszOtherNames[dwIndex]))
  652. return TRUE;
  653. ++dwIndex;
  654. }
  655. }
  656. return FALSE;
  657. }
  658. BOOL
  659. GetSid(
  660. PHANDLE phToken
  661. )
  662. {
  663. if (!OpenThreadToken(GetCurrentThread(),
  664. TOKEN_IMPERSONATE | TOKEN_QUERY,
  665. TRUE,
  666. phToken)) {
  667. DBGMSG(DBG_WARNING, ("OpenThreadToken failed: %d\n", GetLastError()));
  668. return FALSE;
  669. } else
  670. return TRUE;
  671. }
  672. BOOL
  673. SetCurrentSid(
  674. HANDLE hToken
  675. )
  676. {
  677. #if DBG
  678. WCHAR UserName[256];
  679. DWORD cbUserName=256;
  680. if( MODULE_DEBUG & DBG_TRACE )
  681. GetUserName(UserName, &cbUserName);
  682. DBGMSG(DBG_TRACE, ("SetCurrentSid BEFORE: user name is %ws\n", UserName));
  683. #endif
  684. //
  685. // Normally the function SetCurrentSid is not supposed to change the last error
  686. // of the routine where it is called. NtSetInformationThread conveniently returns
  687. // a status and does not touch the last error.
  688. //
  689. NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken,
  690. &hToken, sizeof(hToken));
  691. #if DBG
  692. cbUserName = 256;
  693. if( MODULE_DEBUG & DBG_TRACE )
  694. GetUserName(UserName, &cbUserName);
  695. DBGMSG(DBG_TRACE, ("SetCurrentSid AFTER: user name is %ws\n", UserName));
  696. #endif
  697. return TRUE;
  698. }
  699. LPWSTR
  700. GetErrorString(
  701. DWORD Error
  702. )
  703. {
  704. WCHAR Buffer1[512];
  705. LPWSTR pErrorString=NULL;
  706. DWORD dwFlags;
  707. HANDLE hModule = NULL;
  708. if ((Error >= NERR_BASE) && (Error <= MAX_NERR)) {
  709. dwFlags = FORMAT_MESSAGE_FROM_HMODULE;
  710. hModule = LoadLibrary(szNetMsgDll);
  711. } else {
  712. dwFlags = FORMAT_MESSAGE_FROM_SYSTEM;
  713. hModule = NULL;
  714. }
  715. //
  716. // Only display out of paper and device disconnected errors.
  717. //
  718. if ((Error == ERROR_NOT_READY ||
  719. Error == ERROR_OUT_OF_PAPER ||
  720. Error == ERROR_DEVICE_REINITIALIZATION_NEEDED ||
  721. Error == ERROR_DEVICE_REQUIRES_CLEANING ||
  722. Error == ERROR_DEVICE_DOOR_OPEN ||
  723. Error == ERROR_DEVICE_NOT_CONNECTED) &&
  724. FormatMessage(dwFlags,
  725. hModule,
  726. Error,
  727. 0,
  728. Buffer1,
  729. COUNTOF(Buffer1),
  730. NULL)) {
  731. EnterSplSem();
  732. pErrorString = AllocSplStr(Buffer1);
  733. LeaveSplSem();
  734. }
  735. if (hModule) {
  736. FreeLibrary(hModule);
  737. }
  738. return pErrorString;
  739. }
  740. #define NULL_TERMINATED 0
  741. INT
  742. AnsiToUnicodeString(
  743. LPSTR pAnsi,
  744. LPWSTR pUnicode,
  745. DWORD StringLength
  746. )
  747. /*++
  748. Routine Description:
  749. Converts an Ansi String to a UnicodeString
  750. Arguments:
  751. pAnsi - A valid source ANSI string.
  752. pUnicode - A pointer to a buffer large enough to accommodate
  753. the converted string.
  754. StringLength - The length of the source ANSI string.
  755. If 0 (NULL_TERMINATED), the string is assumed to be
  756. null-terminated.
  757. Return Value:
  758. The return value from MultiByteToWideChar, the number of
  759. wide characters returned.
  760. andrewbe, 11 Jan 1993
  761. --*/
  762. {
  763. if( StringLength == NULL_TERMINATED )
  764. StringLength = strlen( pAnsi );
  765. return MultiByteToWideChar( CP_ACP,
  766. MB_PRECOMPOSED,
  767. pAnsi,
  768. StringLength + 1,
  769. pUnicode,
  770. StringLength + 1 );
  771. }
  772. INT
  773. Message(
  774. HWND hwnd,
  775. DWORD Type,
  776. int CaptionID,
  777. int TextID, ...)
  778. {
  779. /*++
  780. Routine Description:
  781. Displays a message by loading the strings whose IDs are passed into
  782. the function, and substituting the supplied variable argument list
  783. using the varargs macros.
  784. Arguments:
  785. hwnd Window Handle
  786. Type
  787. CaptionID
  788. TextId
  789. Return Value:
  790. --*/
  791. WCHAR MsgText[512];
  792. WCHAR MsgFormat[256];
  793. WCHAR MsgCaption[40];
  794. va_list vargs;
  795. if( ( LoadString( hInst, TextID, MsgFormat,
  796. sizeof MsgFormat / sizeof *MsgFormat ) > 0 )
  797. && ( LoadString( hInst, CaptionID, MsgCaption,
  798. sizeof MsgCaption / sizeof *MsgCaption ) > 0 ) )
  799. {
  800. va_start( vargs, TextID );
  801. _vsntprintf( MsgText, COUNTOF(MsgText)-1, MsgFormat, vargs );
  802. MsgText[COUNTOF(MsgText)-1] = 0;
  803. va_end( vargs );
  804. return MessageBox(hwnd, MsgText, MsgCaption, Type);
  805. }
  806. else
  807. return 0;
  808. }
  809. typedef struct {
  810. DWORD Message;
  811. WPARAM wParam;
  812. LPARAM lParam;
  813. } MESSAGE, *PMESSAGE;
  814. // The Broadcasts are done on a separate thread, the reason it CSRSS
  815. // will create a server side thread when we call user and we don't want
  816. // that to be paired up with the RPC thread which is in the spooss server.
  817. // We want it to go away the moment we have completed the SendMessage.
  818. // We also call SendNotifyMessage since we don't care if the broadcasts
  819. // are syncronous this uses less resources since usually we don't have more
  820. // than one broadcast.
  821. //
  822. // TESTING
  823. //
  824. DWORD dwSendFormMessage = 0;
  825. VOID
  826. SplBroadcastChange(
  827. HANDLE hPrinter,
  828. DWORD Message,
  829. WPARAM wParam,
  830. LPARAM lParam
  831. )
  832. {
  833. PSPOOL pSpool = (PSPOOL)hPrinter;
  834. PINISPOOLER pIniSpooler;
  835. if (ValidateSpoolHandle( pSpool, 0 )) {
  836. pIniSpooler = pSpool->pIniSpooler;
  837. BroadcastChange(pIniSpooler, Message, wParam, lParam);
  838. }
  839. }
  840. VOID
  841. BroadcastChange(
  842. IN PINISPOOLER pIniSpooler,
  843. IN DWORD Message,
  844. IN WPARAM wParam,
  845. IN LPARAM lParam
  846. )
  847. {
  848. if (( pIniSpooler != NULL ) && ( pIniSpooler->SpoolerFlags & SPL_BROADCAST_CHANGE )) {
  849. BOOL bIsTerminalServerInstalled = (USER_SHARED_DATA->SuiteMask & (1 << TerminalServer));
  850. //
  851. // Currently we cannot determine if the TermService process is running, so at the momemt
  852. // we assume it is always running.
  853. //
  854. BOOL bIsTerminalServerRunning = TRUE;
  855. //
  856. // If terminal server is installed and enabled then load the winsta.dll if not already
  857. // loaded and get the send window message function.
  858. //
  859. if ( bIsTerminalServerInstalled && !pfWinStationSendWindowMessage ) {
  860. //
  861. // The winstadllhandle is shared among other files in the spooler, so don't
  862. // load the dll again if it is already loaded. Note: we are not in a critical
  863. // section because winsta.dll is never unload, hence if there are two threads
  864. // that execute this code at the same time we may potenially load the library
  865. // twice.
  866. //
  867. if ( !WinStaDllHandle ) {
  868. UINT uOldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  869. WinStaDllHandle = LoadLibraryW(L"winsta.dll");
  870. SetErrorMode(uOldErrorMode);
  871. }
  872. if ( WinStaDllHandle ) {
  873. pfWinStationSendWindowMessage = (pfnWinStationSendWindowMessage)GetProcAddress( WinStaDllHandle,
  874. "WinStationSendWindowMessage" );
  875. }
  876. }
  877. if ( pfWinStationSendWindowMessage ) {
  878. //
  879. // Only send the message to the session that orginated
  880. // the call, this will go to the console session when a
  881. // change is made by a remote client.
  882. //
  883. LONG Response = 0;
  884. LONG lRetval = FALSE;
  885. HANDLE hToken = NULL;
  886. ULONG uSession = GetClientSessionId();
  887. //
  888. // It appears the WinStationSendWindowMessage function has to
  889. // be in system context if the impersonating user is not an
  890. // an admin on the machine.
  891. //
  892. hToken = RevertToPrinterSelf();
  893. lRetval = pfWinStationSendWindowMessage( SERVERNAME_CURRENT,
  894. uSession,
  895. 1, // Wait at most one second
  896. HandleToULong(HWND_BROADCAST),
  897. Message,
  898. wParam,
  899. lParam,
  900. &Response );
  901. ImpersonatePrinterClient(hToken);
  902. }
  903. //
  904. // We send the message normally if we have a null pfWinstationSendWindowMessage
  905. // function or if terminal server is not running.
  906. //
  907. if ( !pfWinStationSendWindowMessage || !bIsTerminalServerRunning ){
  908. SendNotifyMessage( HWND_BROADCAST,
  909. Message,
  910. wParam,
  911. lParam );
  912. }
  913. } else {
  914. DBGMSG(DBG_TRACE, ("BroadCastChange Ignoring Change\n"));
  915. }
  916. }
  917. VOID
  918. MyMessageBeep(
  919. DWORD fuType,
  920. PINISPOOLER pIniSpooler
  921. )
  922. {
  923. if ( pIniSpooler->dwBeepEnabled != 0 ) {
  924. MessageBeep(fuType);
  925. }
  926. }
  927. // Recursively delete any subkeys of a given key.
  928. // Assumes that RevertToPrinterSelf() has been called.
  929. DWORD
  930. DeleteSubkeys(
  931. HKEY hKey,
  932. PINISPOOLER pIniSpooler
  933. )
  934. {
  935. DWORD cchData;
  936. WCHAR SubkeyName[MAX_PATH];
  937. HKEY hSubkey;
  938. LONG Status;
  939. cchData = COUNTOF( SubkeyName );
  940. while ((Status = SplRegEnumKey( hKey,
  941. 0,
  942. SubkeyName,
  943. &cchData,
  944. NULL,
  945. pIniSpooler )) == ERROR_SUCCESS ) {
  946. Status = SplRegCreateKey( hKey,
  947. SubkeyName,
  948. 0,
  949. KEY_READ | KEY_WRITE,
  950. NULL,
  951. &hSubkey,
  952. NULL,
  953. pIniSpooler );
  954. if( Status == ERROR_SUCCESS ) {
  955. Status = DeleteSubkeys( hSubkey, pIniSpooler );
  956. SplRegCloseKey( hSubkey, pIniSpooler);
  957. if( Status == ERROR_SUCCESS )
  958. SplRegDeleteKey( hKey, SubkeyName, pIniSpooler );
  959. }
  960. //
  961. // N.B. Don't increment since we've deleted the zeroth item.
  962. //
  963. cchData = COUNTOF( SubkeyName );
  964. }
  965. if( Status == ERROR_NO_MORE_ITEMS )
  966. Status = ERROR_SUCCESS;
  967. return Status;
  968. }
  969. long Myatol(LPWSTR nptr)
  970. {
  971. int c; // current char
  972. long total; // current total
  973. int sign; // if '-', then negative, otherwise positive
  974. // skip whitespace
  975. while (isspace(*nptr))
  976. ++nptr;
  977. c = *nptr++;
  978. sign = c; // save sign indication
  979. if (c == '-' || c == '+')
  980. c = *nptr++; // skip sign
  981. total = 0;
  982. while (isdigit(c)) {
  983. total = 10 * total + (c - '0'); // accumulate digit
  984. c = *nptr++; // get next char
  985. }
  986. if (sign == '-')
  987. return -total;
  988. else
  989. return total; // return result, negated if necessary
  990. }
  991. ULONG_PTR
  992. atox(
  993. LPCWSTR psz
  994. )
  995. /*++
  996. Routine Description:
  997. Converts a string to a hex value, skipping any leading
  998. white space. Cannot be uppercase, cannot contain leading 0x.
  999. Arguments:
  1000. psz - pointer to hex string that needs to be converted. This string
  1001. can have leading characters, but MUST be lowercase.
  1002. Return Value:
  1003. DWORD value.
  1004. --*/
  1005. {
  1006. ULONG_PTR Value = 0;
  1007. ULONG_PTR Add;
  1008. _wcslwr((LPWSTR)psz);
  1009. while( isspace( *psz )){
  1010. ++psz;
  1011. }
  1012. for( ;; ++psz ){
  1013. if( *psz >= TEXT( '0' ) && *psz <= TEXT( '9' )){
  1014. Add = *psz - TEXT( '0' );
  1015. } else if( *psz >= TEXT( 'a' ) && *psz <= TEXT( 'f' )){
  1016. Add = *psz - TEXT( 'a' ) + 0xa;
  1017. } else {
  1018. break;
  1019. }
  1020. Value *= 0x10;
  1021. Value += Add;
  1022. }
  1023. return Value;
  1024. }
  1025. BOOL
  1026. ValidateSpoolHandle(
  1027. PSPOOL pSpool,
  1028. DWORD dwDisallowMask
  1029. )
  1030. {
  1031. BOOL ReturnValue;
  1032. try {
  1033. //
  1034. // Zombied handles should return back error. The client
  1035. // side will see ERROR_INVALID_HANDLE, close it and revalidate.
  1036. //
  1037. if (( pSpool == NULL ) ||
  1038. ( pSpool == INVALID_HANDLE_VALUE ) ||
  1039. ( pSpool->Status & SPOOL_STATUS_ZOMBIE ) ||
  1040. ( pSpool->signature != SJ_SIGNATURE ) ||
  1041. ( pSpool->TypeofHandle & dwDisallowMask ) ||
  1042. ( pSpool->TypeofHandle & PRINTER_HANDLE_XCV_PORT ) ||
  1043. ( pSpool->pIniSpooler->signature != ISP_SIGNATURE ) ||
  1044. ( ( pSpool->TypeofHandle & PRINTER_HANDLE_PRINTER ) &&
  1045. ( pSpool->pIniPrinter->signature !=IP_SIGNATURE ) )) {
  1046. ReturnValue = FALSE;
  1047. } else {
  1048. ReturnValue = TRUE;
  1049. }
  1050. }except (1) {
  1051. ReturnValue = FALSE;
  1052. }
  1053. if ( !ReturnValue )
  1054. SetLastError( ERROR_INVALID_HANDLE );
  1055. return ReturnValue;
  1056. }
  1057. BOOL
  1058. UpdateString(
  1059. LPWSTR* ppszCur,
  1060. LPWSTR pszNew)
  1061. {
  1062. //
  1063. // !! LATER !!
  1064. //
  1065. // Replace with non-nls wcscmp since we want byte comparison and
  1066. // only care if the strings are different (ignore ordering).
  1067. //
  1068. if ((!*ppszCur || !**ppszCur) && (!pszNew || !*pszNew))
  1069. return FALSE;
  1070. if (!*ppszCur || !pszNew || wcscmp(*ppszCur, pszNew)) {
  1071. ReallocSplStr(ppszCur, pszNew);
  1072. return TRUE;
  1073. }
  1074. return FALSE;
  1075. }
  1076. BOOL
  1077. CreateDirectoryWithoutImpersonatingUser(
  1078. LPWSTR pDirectory
  1079. )
  1080. /*++
  1081. Routine Description:
  1082. This routine stops impersonating the user and creates a directory
  1083. Arguments:
  1084. pDirectory - Fully Qualified path of directory.
  1085. Return Value:
  1086. TRUE - Success
  1087. FALSE - failed ( call GetLastError )
  1088. --*/
  1089. {
  1090. HANDLE hToken = INVALID_HANDLE_VALUE;
  1091. BOOL bReturnValue;
  1092. SPLASSERT( pDirectory != NULL );
  1093. hToken = RevertToPrinterSelf();
  1094. bReturnValue = CreateDirectory( pDirectory, NULL );
  1095. if ( bReturnValue == FALSE ) {
  1096. DBGMSG( DBG_WARNING, ("CreateDirectoryWithoutImpersonatingUser failed CreateDirectory %ws error %d\n", pDirectory, GetLastError() ));
  1097. }
  1098. if ( hToken != INVALID_HANDLE_VALUE ) {
  1099. ImpersonatePrinterClient(hToken);
  1100. }
  1101. return bReturnValue;
  1102. }
  1103. BOOL
  1104. DeleteAllFilesInDirectory(
  1105. LPWSTR pDirectory,
  1106. BOOL bWaitForReboot
  1107. )
  1108. /*++
  1109. Routine Description:
  1110. Deletes all files the specified directory
  1111. If it can't be deleted it gets marked for deletion on next reboot.
  1112. Arguments:
  1113. pDirectory - Fully Qualified path of directory.
  1114. bWaitForReboot - Don't delete the files until a reboot
  1115. Return Value:
  1116. TRUE - Success
  1117. FALSE - failed something major, like allocating memory.
  1118. --*/
  1119. {
  1120. BOOL bReturnValue = FALSE;
  1121. HANDLE hFindFile;
  1122. WIN32_FIND_DATA FindData;
  1123. WCHAR ScratchBuffer[ MAX_PATH ];
  1124. DBGMSG( DBG_TRACE, ("DeleteAllFilesInDirectory: bWaitForReboot = %\n", bWaitForReboot ));
  1125. SPLASSERT( pDirectory != NULL );
  1126. if (StrNCatBuff(ScratchBuffer, COUNTOF(ScratchBuffer), pDirectory, L"\\*", NULL) != ERROR_SUCCESS)
  1127. return FALSE;
  1128. hFindFile = FindFirstFile( ScratchBuffer, &FindData );
  1129. if ( hFindFile != INVALID_HANDLE_VALUE ) {
  1130. do {
  1131. //
  1132. // Don't Attempt to Delete Directories
  1133. //
  1134. if ( !( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) {
  1135. //
  1136. // Fully Qualified Path
  1137. //
  1138. if (StrNCatBuff( ScratchBuffer,
  1139. COUNTOF(ScratchBuffer),
  1140. pDirectory,
  1141. L"\\",
  1142. FindData.cFileName,
  1143. NULL) == ERROR_SUCCESS) {
  1144. if ( bWaitForReboot || !DeleteFile( ScratchBuffer ) ) {
  1145. DBGMSG( DBG_WARNING, ("DeleteAllFilesInDirectory failed DeleteFile( %ws ) error %d\n", ScratchBuffer, GetLastError() ));
  1146. if ( !MoveFileEx( ScratchBuffer, NULL, MOVEFILE_DELAY_UNTIL_REBOOT ) ) {
  1147. DBGMSG( DBG_WARNING, ("DeleteAllFilesInDirectory failed MoveFileEx %ws error %d\n", ScratchBuffer, GetLastError() ));
  1148. } else {
  1149. DBGMSG( DBG_TRACE, ("MoveFileEx %ws Delay until reboot OK\n", ScratchBuffer ));
  1150. }
  1151. } else {
  1152. DBGMSG( DBG_TRACE, ("Deleted %ws OK\n", ScratchBuffer ));
  1153. }
  1154. }
  1155. }
  1156. } while( FindNextFile( hFindFile, &FindData ) );
  1157. bReturnValue = FindClose( hFindFile );
  1158. } else {
  1159. DBGMSG( DBG_WARNING, ("DeleteOldDrivers failed findfirst ( %ws ), error %d\n", ScratchBuffer, GetLastError() ));
  1160. }
  1161. return bReturnValue;
  1162. }
  1163. BOOL
  1164. DeleteAllFilesAndDirectory(
  1165. LPWSTR pDirectory,
  1166. BOOL bWaitForReboot
  1167. )
  1168. /*++
  1169. Routine Description:
  1170. Deletes all files the specified directory, then deletes the directory.
  1171. If the Directory cannot be deleted right away, it is set to be deleted
  1172. at reboot time.
  1173. Security NOTE - This routine runs as SYSTEM, not imperonating the user
  1174. Arguments:
  1175. pDirectory - Fully Qualified path of directory.
  1176. Return Value:
  1177. TRUE - Success
  1178. FALSE - failed something major, like allocating memory.
  1179. --*/
  1180. {
  1181. BOOL bReturnValue;
  1182. HANDLE hToken = INVALID_HANDLE_VALUE;
  1183. DBGMSG( DBG_TRACE, ("DeleteAllFilesAndDirectory: bWaitForReboot = %d\n", bWaitForReboot ));
  1184. hToken = RevertToPrinterSelf();
  1185. if( bReturnValue = DeleteAllFilesInDirectory( pDirectory, bWaitForReboot ) ) {
  1186. if ( bWaitForReboot || !RemoveDirectory( pDirectory )) {
  1187. if (!SplMoveFileEx( pDirectory, NULL, MOVEFILE_DELAY_UNTIL_REBOOT )) {
  1188. DBGMSG( DBG_WARNING, ("DeleteAllFilesAndDirectory failed to delete %ws until reboot %d\n", pDirectory, GetLastError() ));
  1189. } else {
  1190. DBGMSG( DBG_TRACE, ( "DeleteAllFilesAndDirectory: MoveFileEx Delay until reboot OK\n" ));
  1191. }
  1192. } else {
  1193. DBGMSG( DBG_TRACE, ("DeleteAllFilesAndDirectory deleted %ws OK\n", pDirectory ));
  1194. }
  1195. }
  1196. if ( hToken != INVALID_HANDLE_VALUE ) {
  1197. ImpersonatePrinterClient(hToken);
  1198. }
  1199. return bReturnValue;
  1200. }
  1201. VOID
  1202. DeleteDirectoryRecursively(
  1203. LPCWSTR pszDirectory,
  1204. BOOL bWaitForReboot
  1205. )
  1206. /*++
  1207. Routine Name:
  1208. DeleteDirectoryRecursively
  1209. Routine Description:
  1210. Recursively Deletes the specified directory
  1211. If it can't be deleted it gets marked for deletion on next reboot.
  1212. Arguments:
  1213. pDirectory - Fully Qualified path of directory.
  1214. bWaitForReboot - Don't delete the files until a reboot
  1215. Return Value:
  1216. Nothing.
  1217. --*/
  1218. {
  1219. HANDLE hFindFile;
  1220. WIN32_FIND_DATA FindData;
  1221. WCHAR ScratchBuffer[ MAX_PATH ];
  1222. if ( pszDirectory &&
  1223. StrNCatBuff(ScratchBuffer,
  1224. COUNTOF(ScratchBuffer),
  1225. pszDirectory,
  1226. L"\\*",
  1227. NULL) == ERROR_SUCCESS ) {
  1228. hFindFile = FindFirstFile(ScratchBuffer, &FindData);
  1229. if ( hFindFile != INVALID_HANDLE_VALUE ) {
  1230. do {
  1231. //
  1232. // Don't delete current and parent directory.
  1233. //
  1234. if (wcscmp(FindData.cFileName, L".") != 0 &&
  1235. wcscmp(FindData.cFileName, L"..") != 0 &&
  1236. StrNCatBuff( ScratchBuffer,
  1237. COUNTOF(ScratchBuffer),
  1238. pszDirectory,
  1239. L"\\",
  1240. FindData.cFileName,
  1241. NULL) == ERROR_SUCCESS) {
  1242. if (!(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  1243. if (bWaitForReboot || !DeleteFile(ScratchBuffer)) {
  1244. //
  1245. // Delete the file on reboot if asked or if deletion failed.
  1246. //
  1247. SplMoveFileEx(ScratchBuffer, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
  1248. }
  1249. } else {
  1250. //
  1251. // Delete subdirectory
  1252. //
  1253. DeleteAllFilesAndDirectory(ScratchBuffer, bWaitForReboot);
  1254. }
  1255. }
  1256. } while (FindNextFile(hFindFile, &FindData));
  1257. FindClose(hFindFile);
  1258. if (bWaitForReboot || !RemoveDirectory(pszDirectory)) {
  1259. //
  1260. // Delete the directory on reboot if asked or if deletion failed.
  1261. //
  1262. SplMoveFileEx(pszDirectory, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
  1263. }
  1264. }
  1265. }
  1266. return;
  1267. }
  1268. DWORD
  1269. CreateNumberedTempDirectory(
  1270. IN LPCWSTR pszDirectory,
  1271. OUT LPWSTR *ppszTempDirectory
  1272. )
  1273. /*++
  1274. Routine Name:
  1275. CreateNumberedTempDirectory
  1276. Routine Description:
  1277. Creates a temporary subdirectory named 1... 500
  1278. The lenght of ppTempDirectory cannot be bigger than MAX_PATH
  1279. Returns the number of directory created or -1 for failure
  1280. Arguments:
  1281. pszDirectory - directory where to created temporary
  1282. ppszTempDirectory - path of the new temporary directory
  1283. Return Value:
  1284. If success, returns the number of directory created.
  1285. Returns -1 if a failure occurs.
  1286. --*/
  1287. {
  1288. DWORD dwIndex, dwTempDir;
  1289. WCHAR szTempDir[4];
  1290. WCHAR *pszTemporary = NULL;
  1291. dwTempDir = -1;
  1292. if (pszDirectory && ppszTempDirectory)
  1293. {
  1294. *ppszTempDirectory = NULL;
  1295. if (pszTemporary = AllocSplMem((wcslen(pszDirectory) + COUNTOF(szTempDir) + 1) * sizeof(WCHAR)))
  1296. {
  1297. for (dwIndex = 1; dwIndex < 500; ++dwIndex)
  1298. {
  1299. _itow(dwIndex, szTempDir, 10);
  1300. if (StrNCatBuff(pszTemporary,
  1301. MAX_PATH,
  1302. pszDirectory,
  1303. L"\\",
  1304. szTempDir,
  1305. NULL) == ERROR_SUCCESS &&
  1306. !DirectoryExists(pszTemporary) &&
  1307. CreateDirectory(pszTemporary, NULL))
  1308. {
  1309. dwTempDir = dwIndex;
  1310. break;
  1311. }
  1312. }
  1313. }
  1314. }
  1315. if (dwTempDir != -1)
  1316. {
  1317. *ppszTempDirectory = pszTemporary;
  1318. }
  1319. else
  1320. {
  1321. SetLastError(ERROR_NO_SYSTEM_RESOURCES);
  1322. FreeSplMem(pszTemporary);
  1323. }
  1324. return dwTempDir;
  1325. }
  1326. //
  1327. // Dependent file fields are LPWSTR field of filenames separated by \0
  1328. // and terminated by \0\0
  1329. // 2 such fields are same if same set of filenames appear
  1330. // (order of filenames does not matter)
  1331. //
  1332. BOOL
  1333. SameMultiSz(
  1334. LPWSTR pszz1,
  1335. LPWSTR pszz2
  1336. )
  1337. {
  1338. LPWSTR psz1, psz2;
  1339. if ( !pszz1 && !pszz2 )
  1340. return TRUE;
  1341. if ( !pszz1 || !pszz2 )
  1342. return FALSE;
  1343. //
  1344. // Check there are same number of strings
  1345. //
  1346. for ( psz1 = pszz1, psz2 = pszz2 ;
  1347. *psz1 && *psz2 ;
  1348. psz1 += wcslen(psz1)+1, psz2 += wcslen(psz2)+1 )
  1349. ;
  1350. //
  1351. // If different number of strings return FALSE
  1352. //
  1353. if ( *psz1 || *psz2 )
  1354. return FALSE;
  1355. //
  1356. // Check in pszz2 for each string in pszz1
  1357. //
  1358. for ( psz1 = pszz1 ; *psz1 ; psz1 += wcslen(psz1) + 1 ) {
  1359. for ( psz2 = pszz2 ;
  1360. *psz2 && _wcsicmp(psz1, psz2) ;
  1361. psz2 += wcslen(psz2) + 1 )
  1362. ;
  1363. //
  1364. // Did we find psz1 in pszz2
  1365. //
  1366. if ( ! *psz2 ) {
  1367. return FALSE;
  1368. }
  1369. }
  1370. return TRUE;
  1371. }
  1372. int
  1373. wstrcmpEx(
  1374. LPCWSTR s1,
  1375. LPCWSTR s2,
  1376. BOOL bCaseSensitive
  1377. )
  1378. {
  1379. if ( s1 && *s1 ) {
  1380. if ( s2 && *s2 ) {
  1381. return bCaseSensitive ? wcscmp(s1, s2) : _wcsicmp(s1, s2);
  1382. }
  1383. else {
  1384. return 1;
  1385. }
  1386. }
  1387. else {
  1388. if ( s2 && *s2 ) {
  1389. return -1;
  1390. }
  1391. else {
  1392. return 0;
  1393. }
  1394. }
  1395. }
  1396. BOOL
  1397. RegSetString(
  1398. HANDLE hKey,
  1399. LPWSTR pValueName,
  1400. LPWSTR pStringValue,
  1401. PDWORD pdwLastError,
  1402. PINISPOOLER pIniSpooler
  1403. )
  1404. {
  1405. BOOL bReturnValue;
  1406. LPWSTR pString;
  1407. DWORD cbString;
  1408. DWORD Status;
  1409. if ( pStringValue ) {
  1410. pString = pStringValue;
  1411. cbString = ( wcslen( pStringValue ) + 1 )*sizeof(WCHAR);
  1412. } else {
  1413. pString = szNull;
  1414. cbString = sizeof(WCHAR);
  1415. }
  1416. Status = SplRegSetValue( hKey,
  1417. pValueName,
  1418. REG_SZ,
  1419. (LPBYTE)pString,
  1420. cbString,
  1421. pIniSpooler );
  1422. if ( Status != ERROR_SUCCESS ) {
  1423. DBGMSG( DBG_WARNING, ("RegSetString value %ws string %ws error %d\n", pValueName, pString, Status ));
  1424. *pdwLastError = Status;
  1425. bReturnValue = FALSE;
  1426. } else {
  1427. bReturnValue = TRUE;
  1428. }
  1429. return bReturnValue;
  1430. }
  1431. BOOL
  1432. RegSetDWord(
  1433. HANDLE hKey,
  1434. LPWSTR pValueName,
  1435. DWORD dwParam,
  1436. PDWORD pdwLastError,
  1437. PINISPOOLER pIniSpooler
  1438. )
  1439. {
  1440. BOOL bReturnValue;
  1441. LPWSTR pString;
  1442. DWORD Status;
  1443. Status = SplRegSetValue( hKey,
  1444. pValueName,
  1445. REG_DWORD,
  1446. (LPBYTE)&dwParam,
  1447. sizeof(DWORD),
  1448. pIniSpooler );
  1449. if ( Status != ERROR_SUCCESS ) {
  1450. DBGMSG( DBG_WARNING, ("RegSetDWord value %ws DWORD %x error %d\n",
  1451. pValueName, dwParam, Status ));
  1452. *pdwLastError = Status;
  1453. bReturnValue = FALSE;
  1454. } else {
  1455. bReturnValue = TRUE;
  1456. }
  1457. return bReturnValue;
  1458. }
  1459. BOOL
  1460. RegSetBinaryData(
  1461. HKEY hKey,
  1462. LPWSTR pValueName,
  1463. LPBYTE pData,
  1464. DWORD cbData,
  1465. PDWORD pdwLastError,
  1466. PINISPOOLER pIniSpooler
  1467. )
  1468. {
  1469. DWORD Status;
  1470. BOOL bReturnValue;
  1471. Status = SplRegSetValue( hKey,
  1472. pValueName,
  1473. REG_BINARY,
  1474. pData,
  1475. cbData,
  1476. pIniSpooler );
  1477. if ( Status != ERROR_SUCCESS ) {
  1478. DBGMSG( DBG_WARNING, ("RegSetBinaryData Value %ws pData %x cbData %d error %d\n",
  1479. pValueName,
  1480. pData,
  1481. cbData,
  1482. Status ));
  1483. bReturnValue = FALSE;
  1484. *pdwLastError = Status;
  1485. } else {
  1486. bReturnValue = TRUE;
  1487. }
  1488. return bReturnValue;
  1489. }
  1490. BOOL
  1491. RegSetMultiString(
  1492. HANDLE hKey,
  1493. LPWSTR pValueName,
  1494. LPWSTR pStringValue,
  1495. DWORD cchString,
  1496. PDWORD pdwLastError,
  1497. PINISPOOLER pIniSpooler
  1498. )
  1499. {
  1500. BOOL bReturnValue;
  1501. DWORD Status;
  1502. LPWSTR pString;
  1503. WCHAR szzNull[2];
  1504. if ( pStringValue ) {
  1505. pString = pStringValue;
  1506. cchString *= sizeof(WCHAR);
  1507. } else {
  1508. szzNull[0] = szzNull[1] = '\0';
  1509. pString = szNull;
  1510. cchString = 2 * sizeof(WCHAR);
  1511. }
  1512. Status = SplRegSetValue( hKey,
  1513. pValueName,
  1514. REG_MULTI_SZ,
  1515. (LPBYTE)pString,
  1516. cchString,
  1517. pIniSpooler );
  1518. if ( Status != ERROR_SUCCESS ) {
  1519. DBGMSG( DBG_WARNING, ("RegSetMultiString value %ws string %ws error %d\n", pValueName, pString, Status ));
  1520. *pdwLastError = Status;
  1521. bReturnValue = FALSE;
  1522. } else {
  1523. bReturnValue = TRUE;
  1524. }
  1525. return bReturnValue;
  1526. }
  1527. BOOL
  1528. RegGetString(
  1529. HANDLE hKey,
  1530. LPWSTR pValueName,
  1531. LPWSTR *ppValue,
  1532. LPDWORD pcchValue,
  1533. PDWORD pdwLastError,
  1534. BOOL bFailIfNotFound,
  1535. PINISPOOLER pIniSpooler
  1536. )
  1537. /*++
  1538. Routine Description:
  1539. Allocates memory and reads a value from Registry for a value which was
  1540. earlier set by calling RegSetValueEx.
  1541. Arguments:
  1542. hKey : currently open key to be used to query the registry
  1543. pValueName : value to be used to query the registry
  1544. ppValue : on return value of TRUE *ppValue (memory allocated by
  1545. the routine) will have the value
  1546. pdwLastError : on failure *dwLastError will give the error
  1547. bFailIfNotFound : Tells if the field is mandatory (if not found error)
  1548. Return Value:
  1549. TRUE : value is found and succesfully read.
  1550. Memory will be allocated to hold the value
  1551. FALSE: Value was not read.
  1552. If bFailIfNotFound was TRUE error code will be set.
  1553. History:
  1554. Written by MuhuntS (Muhunthan Sivapragasam)June 95
  1555. --*/
  1556. {
  1557. BOOL bReturnValue = TRUE;
  1558. LPWSTR pString;
  1559. DWORD cbValue;
  1560. DWORD Status, Type;
  1561. //
  1562. // First query to find out size
  1563. //
  1564. cbValue = 0;
  1565. Status = SplRegQueryValue( hKey,
  1566. pValueName,
  1567. &Type,
  1568. NULL,
  1569. &cbValue,
  1570. pIniSpooler );
  1571. if ( Status != ERROR_SUCCESS ) {
  1572. // Set error code only if it is a required field
  1573. if ( bFailIfNotFound )
  1574. *pdwLastError = Status;
  1575. bReturnValue = FALSE;
  1576. } else if ( (Type == REG_SZ && cbValue > sizeof(WCHAR) ) ||
  1577. (Type == REG_MULTI_SZ && cbValue > 2*sizeof(WCHAR)) ) {
  1578. //
  1579. // Something (besides \0 or \0\0) to read
  1580. //
  1581. if ( !(*ppValue=AllocSplMem(cbValue) ) ) {
  1582. *pdwLastError = GetLastError();
  1583. bReturnValue = FALSE;
  1584. } else {
  1585. Status = SplRegQueryValue( hKey,
  1586. pValueName,
  1587. &Type,
  1588. (LPBYTE)*ppValue,
  1589. &cbValue,
  1590. pIniSpooler );
  1591. if ( Status != ERROR_SUCCESS ) {
  1592. DBGMSG( DBG_WARNING, ("RegGetString value %ws string %ws error %d\n", pValueName, **ppValue, Status ));
  1593. *pdwLastError = Status;
  1594. bReturnValue = FALSE;
  1595. } else {
  1596. *pcchValue = cbValue / sizeof(WCHAR);
  1597. bReturnValue = TRUE;
  1598. }
  1599. }
  1600. }
  1601. return bReturnValue;
  1602. }
  1603. BOOL
  1604. RegGetMultiSzString(
  1605. HANDLE hKey,
  1606. LPWSTR pValueName,
  1607. LPWSTR *ppValue,
  1608. LPDWORD pcchValue,
  1609. PDWORD pdwLastError,
  1610. BOOL bFailIfNotFound,
  1611. PINISPOOLER pIniSpooler
  1612. )
  1613. /*++
  1614. Routine Description:
  1615. Duplicate function for RegGetString. Handles multi-sz strings so that Spooler
  1616. doesn't crash.
  1617. Arguments:
  1618. hKey : currently open key to be used to query the registry
  1619. pValueName : value to be used to query the registry
  1620. ppValue : on return value of TRUE *ppValue (memory allocated by
  1621. the routine) will have the value
  1622. pdwLastError : on failure *dwLastError will give the error
  1623. bFailIfNotFound : Tells if the field is mandatory (if not found error)
  1624. Return Value:
  1625. TRUE : value is found and succesfully read.
  1626. Memory will be allocated to hold the value
  1627. FALSE: Value was not read.
  1628. If bFailIfNotFound was TRUE error code will be set.
  1629. History:
  1630. Written by AdinaTru. This function is a fix for the case when 3rd party applications
  1631. install drivers by writing registry string values instead of multi-sz. This causes Spooler
  1632. to AV because it will handle a string as a multi-sz string. The goal of having this function
  1633. was to provide a quick fix/low regression risk for XP RC2 release. A bug was opened for rewriting
  1634. RegGetMultiSzString and RegGetString in BlackComb timeframe.
  1635. --*/
  1636. {
  1637. BOOL bReturnValue = TRUE;
  1638. LPWSTR pString;
  1639. DWORD cbValue;
  1640. DWORD Status, Type;
  1641. //
  1642. // First query to find out size
  1643. //
  1644. cbValue = 0;
  1645. Status = SplRegQueryValue( hKey,
  1646. pValueName,
  1647. &Type,
  1648. NULL,
  1649. &cbValue,
  1650. pIniSpooler );
  1651. if ( Status != ERROR_SUCCESS ) {
  1652. // Set error code only if it is a required field
  1653. if ( bFailIfNotFound )
  1654. *pdwLastError = Status;
  1655. bReturnValue = FALSE;
  1656. } else if ( (Type == REG_SZ && cbValue > sizeof(WCHAR) ) ||
  1657. (Type == REG_MULTI_SZ && cbValue > 2*sizeof(WCHAR)) ) {
  1658. //
  1659. // Something (besides \0 or \0\0) to read
  1660. //
  1661. //
  1662. // We expect a REG_MULTI_SZ string. Add an extra zero so Spooler doesn't crash.
  1663. // XP RC2 fix.
  1664. //
  1665. if (Type == REG_SZ) {
  1666. cbValue += sizeof(WCHAR);
  1667. }
  1668. if ( !(*ppValue=AllocSplMem(cbValue) ) ) {
  1669. *pdwLastError = GetLastError();
  1670. bReturnValue = FALSE;
  1671. } else {
  1672. Status = SplRegQueryValue( hKey,
  1673. pValueName,
  1674. &Type,
  1675. (LPBYTE)*ppValue,
  1676. &cbValue,
  1677. pIniSpooler );
  1678. if ( Status != ERROR_SUCCESS ) {
  1679. DBGMSG( DBG_WARNING, ("RegGetString value %ws string %ws error %d\n", pValueName, **ppValue, Status ));
  1680. *pdwLastError = Status;
  1681. bReturnValue = FALSE;
  1682. //
  1683. // Caller will must the memory regardless of success or failure.
  1684. //
  1685. } else {
  1686. *pcchValue = cbValue / sizeof(WCHAR);
  1687. bReturnValue = TRUE;
  1688. }
  1689. }
  1690. }
  1691. return bReturnValue;
  1692. }
  1693. VOID
  1694. FreeStructurePointers(
  1695. LPBYTE lpStruct,
  1696. LPBYTE lpStruct2,
  1697. LPDWORD lpOffsets)
  1698. /*++
  1699. Routine Description:
  1700. This routine frees memory allocated to all the pointers in the structure
  1701. If lpStruct2 is specified only pointers in lpStruct which are different
  1702. than the ones in lpStruct will be freed
  1703. Arguments:
  1704. lpStruct: Pointer to the structure
  1705. lpStruct2: Pointer to the structure to compare with (optional)
  1706. lpOffsets: An array of DWORDS (terminated by -1) givings offsets in the
  1707. structure which have memory which needs to be freed
  1708. Return Value:
  1709. nothing
  1710. History:
  1711. MuhuntS -- Aug 95
  1712. --*/
  1713. {
  1714. register INT i;
  1715. if ( lpStruct2 ) {
  1716. for( i=0; lpOffsets[i] != 0xFFFFFFFF; ++i ) {
  1717. if ( *(LPBYTE *) (lpStruct+lpOffsets[i]) &&
  1718. *(LPBYTE *) (lpStruct+lpOffsets[i]) !=
  1719. *(LPBYTE *) (lpStruct2+lpOffsets[i]) )
  1720. FreeSplMem(*(LPBYTE *) (lpStruct+lpOffsets[i]));
  1721. }
  1722. } else {
  1723. for( i=0; lpOffsets[i] != 0xFFFFFFFF; ++i ) {
  1724. if ( *(LPBYTE *) (lpStruct+lpOffsets[i]) )
  1725. FreeSplMem(*(LPBYTE *) (lpStruct+lpOffsets[i]));
  1726. }
  1727. }
  1728. }
  1729. /*++
  1730. Routine Name:
  1731. AllocOrUpdateStringAndTestSame
  1732. Routine Description:
  1733. This routine can be used to do an atomic update of values in a structure.
  1734. Create a temporary structure and copy the old structure to it.
  1735. Then call this routine for all LPWSTR fields to check and update strings
  1736. If the value changes:
  1737. This routine will allocate memory and assign pointer in the
  1738. temporary structure.
  1739. Arguments:
  1740. ppString : Points to a pointer in the temporary sturucture
  1741. pNewValue : New value to be set
  1742. pOldValue : Value in the original strucutre
  1743. bCaseSensitive : Determines whether case-sensitive string compare is performed
  1744. pbFail : On error set this to TRUE (Note: it could already be TRUE)
  1745. *pbIdentical : If the strings are diferent this is set to FALSE.
  1746. (Could already be false).
  1747. Return Value:
  1748. TRUE on success, else FALSE
  1749. --*/
  1750. BOOL
  1751. AllocOrUpdateStringAndTestSame(
  1752. IN LPWSTR *ppString,
  1753. IN LPCWSTR pNewValue,
  1754. IN LPCWSTR pOldValue,
  1755. IN BOOL bCaseSensitive,
  1756. IN OUT BOOL *pbFail,
  1757. IN OUT BOOL *pbIdentical
  1758. )
  1759. {
  1760. BOOL bReturn = TRUE;
  1761. int iReturn;
  1762. if ( *pbFail )
  1763. return FALSE;
  1764. if (wstrcmpEx(pNewValue, pOldValue, bCaseSensitive)) {
  1765. *pbIdentical = FALSE;
  1766. if ( pNewValue && *pNewValue ) {
  1767. if ( !(*ppString = AllocSplStr(pNewValue)) ) {
  1768. *pbFail = TRUE;
  1769. bReturn = FALSE;
  1770. }
  1771. } else {
  1772. *ppString = NULL;
  1773. }
  1774. }
  1775. return bReturn;
  1776. }
  1777. /*++
  1778. Routine Name:
  1779. AllocOrUpdateString
  1780. Routine Description:
  1781. This routine can be used to do an atomic update of values in a structure.
  1782. Create a temporary structure and copy the old structure to it.
  1783. Then call this routine for all LPWSTR fields to check and update strings
  1784. If the value changes:
  1785. This routine will allocate memory and assign pointer in the
  1786. temporary structure.
  1787. Arguments:
  1788. ppString : Points to a pointer in the temporary sturucture
  1789. pNewValue : New value to be set
  1790. pOldValue : Value in the original strucutre
  1791. bCaseSensitive : Determines whether case-sensitive string compare is performed
  1792. pbFail : On error set this to TRUE (Note: it could already be TRUE)
  1793. Return Value:
  1794. TRUE on success, else FALSE
  1795. --*/
  1796. BOOL
  1797. AllocOrUpdateString(
  1798. IN LPWSTR *ppString,
  1799. IN LPCWSTR pNewValue,
  1800. IN LPCWSTR pOldValue,
  1801. IN BOOL bCaseSensitive,
  1802. IN OUT BOOL *pbFail
  1803. )
  1804. {
  1805. BOOL bIdentical = FALSE;
  1806. return AllocOrUpdateStringAndTestSame(ppString, pNewValue, pOldValue, bCaseSensitive, pbFail, &bIdentical);
  1807. }
  1808. VOID
  1809. CopyNewOffsets(
  1810. LPBYTE pStruct,
  1811. LPBYTE pTempStruct,
  1812. LPDWORD lpOffsets)
  1813. /*++
  1814. Routine Description:
  1815. This routine can be used to do an atomic update of values in a structure.
  1816. Create a temporary structure and allocate memory for values which
  1817. are being updated in it, and set the remaining pointers to those in
  1818. the original.
  1819. This routine is called at the end to update the structure.
  1820. Arguments:
  1821. pStruct: Pointer to the structure
  1822. pTempStruct: Pointer to the temporary structure
  1823. lpOffsets: An array of DWORDS givings offsets within the stuctures
  1824. Return Value:
  1825. nothing
  1826. History:
  1827. MuhuntS -- Aug 95
  1828. --*/
  1829. {
  1830. register INT i;
  1831. for( i=0; lpOffsets[i] != 0xFFFFFFFF; ++i ) {
  1832. if ( *(LPBYTE *) (pStruct+lpOffsets[i]) !=
  1833. *(LPBYTE *) (pTempStruct+lpOffsets[i]) ) {
  1834. if ( *(LPBYTE *) (pStruct+lpOffsets[i]) )
  1835. FreeSplMem(*(LPBYTE *) (pStruct+lpOffsets[i]));
  1836. *(LPBYTE *) (pStruct+lpOffsets[i]) = *(LPBYTE *) (pTempStruct+lpOffsets[i]);
  1837. }
  1838. }
  1839. }
  1840. DWORD
  1841. GetIniDriverAndDirForThisMachine(
  1842. IN PINIPRINTER pIniPrinter,
  1843. OUT LPWSTR pszDriverDir,
  1844. OUT PINIDRIVER *ppIniDriver
  1845. )
  1846. /*++
  1847. Description:
  1848. Gets the path to the driver directory for the printer on the local machine
  1849. Arguments:
  1850. pIniPrinter - Points to IniPrinter
  1851. pszDriverDir - A buffer of size MAX_PATH to get the directory path
  1852. Return Vlaue:
  1853. Number of characters copied (0 on failure)
  1854. --*/
  1855. {
  1856. PINIVERSION pIniVersion = NULL;
  1857. PINIENVIRONMENT pIniEnvironment;
  1858. PINISPOOLER pIniSpooler = pIniPrinter->pIniSpooler;
  1859. DWORD dwIndex;
  1860. EnterSplSem();
  1861. //
  1862. // Find driver file for the given driver and then get it's fullpath
  1863. //
  1864. SPLASSERT(pIniPrinter && pIniPrinter->pIniDriver && pIniPrinter->pIniDriver->pName);
  1865. pIniEnvironment = FindEnvironment(szEnvironment, pIniSpooler);
  1866. *ppIniDriver = FindCompatibleDriver(pIniEnvironment,
  1867. &pIniVersion,
  1868. pIniPrinter->pIniDriver->pName,
  1869. dwMajorVersion,
  1870. dwUpgradeFlag);
  1871. SPLASSERT(*ppIniDriver);
  1872. dwIndex = GetDriverVersionDirectory(pszDriverDir,
  1873. MAX_PATH - 2,
  1874. pIniPrinter->pIniSpooler,
  1875. pIniEnvironment,
  1876. pIniVersion,
  1877. *ppIniDriver,
  1878. NULL);
  1879. pszDriverDir[dwIndex++] = L'\\';
  1880. pszDriverDir[dwIndex] = L'\0';
  1881. LeaveSplSem();
  1882. return dwIndex;
  1883. }
  1884. LPWSTR
  1885. GetConfigFilePath(
  1886. IN PINIPRINTER pIniPrinter
  1887. )
  1888. /*++
  1889. Description:
  1890. Gets the full path to the config file (driver ui file) associated with the
  1891. driver. Memory is allocated
  1892. Arguments:
  1893. pIniPrinter - Points to IniPrinter
  1894. Return Vlaue:
  1895. Pointer to the printer name buffer (NULL on error)
  1896. --*/
  1897. {
  1898. DWORD dwIndex;
  1899. WCHAR szDriverPath[MAX_PATH + 1];
  1900. PINIDRIVER pIniDriver;
  1901. if ( dwIndex = GetIniDriverAndDirForThisMachine(pIniPrinter,
  1902. szDriverPath,
  1903. &pIniDriver) )
  1904. wcscpy(szDriverPath+dwIndex, pIniDriver->pConfigFile);
  1905. return AllocSplStr(szDriverPath);
  1906. }
  1907. PDEVMODE
  1908. ConvertDevModeToSpecifiedVersion(
  1909. IN PINIPRINTER pIniPrinter,
  1910. IN PDEVMODE pDevMode,
  1911. IN LPWSTR pszConfigFile, OPTIONAL
  1912. IN LPWSTR pszPrinterNameWithToken, OPTIONAL
  1913. IN BOOL bNt35xVersion
  1914. )
  1915. /*++
  1916. Description:
  1917. Calls driver UI routines to get the default devmode and then converts given devmode
  1918. to that version. If the input devmode is in IniPrinter routine makes a copy before
  1919. converting it.
  1920. This routine needs to be called from inside spooler semaphore
  1921. Arguments:
  1922. pIniPrinter - Points to IniPrinter
  1923. pDevMode - Devmode to convert to current version
  1924. pConfigFile - Full path to driver UI file to do LoadLibrary (optional)
  1925. pszPrinterNameWithToken - Name of printer with token (optional)
  1926. bToNt3xVersion - If TRUE devmode is converted to Nt3x format, else to current version
  1927. Return Vlaue:
  1928. Pointer to new devmode on success, NULL on failure
  1929. --*/
  1930. {
  1931. LPWSTR pszLocalConfigFile = pszConfigFile;
  1932. LPWSTR pszLocalPrinterNameWithToken = pszPrinterNameWithToken;
  1933. LPDEVMODE pNewDevMode = NULL, pOldDevMode = NULL;
  1934. DWORD dwNeeded, dwLastError;
  1935. LONG lNeeded;
  1936. HANDLE hDevModeConvert = NULL,hPrinter = NULL;
  1937. BOOL bCallDocumentProperties = FALSE;
  1938. SplInSem();
  1939. //
  1940. // If ConfigFile or PrinterNameWithToken is not given allocate it locally
  1941. //
  1942. if ( !pszLocalConfigFile ) {
  1943. pszLocalConfigFile = GetConfigFilePath(pIniPrinter);
  1944. if ( !pszLocalConfigFile )
  1945. goto Cleanup;
  1946. }
  1947. if ( !pszLocalPrinterNameWithToken ) {
  1948. pszLocalPrinterNameWithToken = pszGetPrinterName( pIniPrinter,
  1949. TRUE,
  1950. pszLocalOnlyToken );
  1951. if ( !pszLocalPrinterNameWithToken )
  1952. goto Cleanup;
  1953. }
  1954. //
  1955. // If we are trying to convert pIniPrinter->pDevMode make a copy since we are going to leave SplSem
  1956. //
  1957. if ( pDevMode ) {
  1958. if ( pDevMode == pIniPrinter->pDevMode ) {
  1959. dwNeeded = pDevMode->dmSize + pDevMode->dmDriverExtra;
  1960. SPLASSERT(dwNeeded == pIniPrinter->cbDevMode);
  1961. pOldDevMode = AllocSplMem(dwNeeded);
  1962. if ( !pOldDevMode )
  1963. goto Cleanup;
  1964. CopyMemory((LPBYTE)pOldDevMode, (LPBYTE)pDevMode, dwNeeded);
  1965. } else {
  1966. pOldDevMode = pDevMode;
  1967. }
  1968. }
  1969. //
  1970. // Driver is going to call OpenPrinter, so leave SplSem
  1971. //
  1972. LeaveSplSem();
  1973. SplOutSem();
  1974. hDevModeConvert = LoadDriverFiletoConvertDevmode(pszLocalConfigFile);
  1975. if ( !hDevModeConvert ) {
  1976. // If the function is not exported and 3.5x conversion is not required
  1977. // the devmode can be got from DocumentProperties
  1978. if ( bNt35xVersion != NT3X_VERSION ) {
  1979. bCallDocumentProperties = TRUE;
  1980. }
  1981. goto CleanupFromOutsideSplSem;
  1982. }
  1983. dwNeeded = 0;
  1984. if ( bNt35xVersion == NT3X_VERSION ) {
  1985. //
  1986. // Call CallDrvDevModeConversion to allocate memory and return 351 devmode
  1987. //
  1988. dwLastError = CallDrvDevModeConversion(hDevModeConvert,
  1989. pszLocalPrinterNameWithToken,
  1990. (LPBYTE)pOldDevMode,
  1991. (LPBYTE *)&pNewDevMode,
  1992. &dwNeeded,
  1993. CDM_CONVERT351,
  1994. TRUE);
  1995. SPLASSERT(dwLastError == ERROR_SUCCESS || !pNewDevMode);
  1996. } else {
  1997. //
  1998. // Call CallDrvDevModeConversion to allocate memory and give default devmode
  1999. dwLastError = CallDrvDevModeConversion(hDevModeConvert,
  2000. pszLocalPrinterNameWithToken,
  2001. NULL,
  2002. (LPBYTE *)&pNewDevMode,
  2003. &dwNeeded,
  2004. CDM_DRIVER_DEFAULT,
  2005. TRUE);
  2006. if ( dwLastError != ERROR_SUCCESS ) {
  2007. SPLASSERT(!pNewDevMode);
  2008. // Call DocumentProperties to get the default devmode
  2009. bCallDocumentProperties = TRUE;
  2010. goto CleanupFromOutsideSplSem;
  2011. }
  2012. //
  2013. // If we have an input devmode to convert to current mode call driver again
  2014. //
  2015. if ( pOldDevMode ) {
  2016. dwLastError = CallDrvDevModeConversion(hDevModeConvert,
  2017. pszLocalPrinterNameWithToken,
  2018. (LPBYTE)pOldDevMode,
  2019. (LPBYTE *)&pNewDevMode,
  2020. &dwNeeded,
  2021. CDM_CONVERT,
  2022. FALSE);
  2023. //
  2024. // If call failed free devmode which was allocated by previous call
  2025. //
  2026. if ( dwLastError != ERROR_SUCCESS ) {
  2027. // Call DocumentProperties to get the default devmode
  2028. bCallDocumentProperties = TRUE;
  2029. goto CleanupFromOutsideSplSem;
  2030. }
  2031. }
  2032. }
  2033. CleanupFromOutsideSplSem:
  2034. if (bCallDocumentProperties) {
  2035. // Get a client side printer handle to pass to the driver
  2036. if (!(* pfnOpenPrinter)(pszLocalPrinterNameWithToken, &hPrinter, NULL)) {
  2037. goto ReEnterSplSem;
  2038. }
  2039. if (!pNewDevMode) {
  2040. // Get the default devmode
  2041. lNeeded = (* pfnDocumentProperties)(NULL,
  2042. hPrinter,
  2043. pszLocalPrinterNameWithToken,
  2044. NULL,
  2045. NULL,
  2046. 0);
  2047. if (lNeeded <= 0 ||
  2048. !(pNewDevMode = (LPDEVMODEW) AllocSplMem(lNeeded)) ||
  2049. (* pfnDocumentProperties)(NULL,
  2050. hPrinter,
  2051. pszLocalPrinterNameWithToken,
  2052. pNewDevMode,
  2053. NULL,
  2054. DM_OUT_BUFFER) < 0) {
  2055. if (pNewDevMode) {
  2056. FreeSplMem(pNewDevMode);
  2057. pNewDevMode = NULL;
  2058. goto ReEnterSplSem;
  2059. }
  2060. }
  2061. }
  2062. if (pOldDevMode) {
  2063. // Convert to Current mode
  2064. if ((* pfnDocumentProperties)(NULL,
  2065. hPrinter,
  2066. pszLocalPrinterNameWithToken,
  2067. pNewDevMode,
  2068. pOldDevMode,
  2069. DM_IN_BUFFER | DM_OUT_BUFFER) < 0) {
  2070. FreeSplMem(pNewDevMode);
  2071. pNewDevMode = NULL;
  2072. goto ReEnterSplSem;
  2073. }
  2074. }
  2075. }
  2076. ReEnterSplSem:
  2077. if (hPrinter) {
  2078. (* pfnClosePrinter)(hPrinter);
  2079. }
  2080. SplOutSem();
  2081. EnterSplSem();
  2082. Cleanup:
  2083. if ( hDevModeConvert )
  2084. UnloadDriverFile(hDevModeConvert);
  2085. if ( pszLocalConfigFile != pszConfigFile )
  2086. FreeSplStr(pszLocalConfigFile);
  2087. if ( pszPrinterNameWithToken != pszLocalPrinterNameWithToken )
  2088. FreeSplStr(pszLocalPrinterNameWithToken);
  2089. if ( pOldDevMode != pDevMode )
  2090. FreeSplMem(pOldDevMode);
  2091. return pNewDevMode;
  2092. }
  2093. BOOL
  2094. IsPortType(
  2095. LPWSTR pPort,
  2096. LPWSTR pPrefix
  2097. )
  2098. {
  2099. DWORD dwLen;
  2100. SPLASSERT(pPort && *pPort && pPrefix && *pPrefix);
  2101. dwLen = wcslen(pPrefix);
  2102. if ( wcslen(pPort) < dwLen ) {
  2103. return FALSE;
  2104. }
  2105. if ( _wcsnicmp(pPort, pPrefix, dwLen) )
  2106. {
  2107. return FALSE;
  2108. }
  2109. //
  2110. // wcslen guarenteed >= 3
  2111. //
  2112. return pPort[ wcslen( pPort ) - 1 ] == L':';
  2113. }
  2114. /* UnicodeToAnsiString
  2115. *
  2116. * Parameters:
  2117. *
  2118. * pUnicode - A valid source Unicode string.
  2119. *
  2120. * pANSI - A pointer to a buffer large enough to accommodate
  2121. * the converted string.
  2122. *
  2123. * StringLength - The length of the source Unicode string.
  2124. * If 0 (NULL_TERMINATED), the string is assumed to be
  2125. * null-terminated.
  2126. *
  2127. *
  2128. * Notes:
  2129. * With DBCS enabled, we will allocate twice the size of the
  2130. * buffer including the null terminator to take care of double
  2131. * byte character strings - KrishnaG
  2132. *
  2133. * pUnicode is truncated to StringLength characters.
  2134. *
  2135. * Return:
  2136. *
  2137. * The return value from WideCharToMultiByte, the number of
  2138. * multi-byte characters returned.
  2139. *
  2140. *
  2141. * andrewbe, 11 Jan 1993
  2142. */
  2143. INT
  2144. UnicodeToAnsiString(
  2145. LPWSTR pUnicode,
  2146. LPSTR pAnsi,
  2147. DWORD StringLength)
  2148. {
  2149. LPSTR pTempBuf = NULL;
  2150. INT rc = 0;
  2151. if( StringLength == NULL_TERMINATED ) {
  2152. //
  2153. // StringLength is just the
  2154. // number of characters in the string
  2155. //
  2156. StringLength = wcslen( pUnicode );
  2157. }
  2158. //
  2159. // WideCharToMultiByte doesn't NULL terminate if we're copying
  2160. // just part of the string, so terminate here.
  2161. //
  2162. pUnicode[StringLength] = 0;
  2163. //
  2164. // Include one for the NULL
  2165. //
  2166. StringLength++;
  2167. //
  2168. // Unfortunately, WideCharToMultiByte doesn't do conversion in place,
  2169. // so allocate a temporary buffer, which we can then copy:
  2170. //
  2171. if( pAnsi == (LPSTR)pUnicode )
  2172. {
  2173. pTempBuf = AllocSplMem(StringLength*2);
  2174. pAnsi = pTempBuf;
  2175. }
  2176. if( pAnsi )
  2177. {
  2178. rc = WideCharToMultiByte( CP_THREAD_ACP,
  2179. 0,
  2180. pUnicode,
  2181. StringLength,
  2182. pAnsi,
  2183. StringLength*2,
  2184. NULL,
  2185. NULL );
  2186. }
  2187. /* If pTempBuf is non-null, we must copy the resulting string
  2188. * so that it looks as if we did it in place:
  2189. */
  2190. if( pTempBuf )
  2191. {
  2192. if( rc > 0 )
  2193. {
  2194. pAnsi = (LPSTR)pUnicode;
  2195. strcpy( pAnsi, pTempBuf );
  2196. }
  2197. FreeSplMem(pTempBuf);
  2198. }
  2199. return rc;
  2200. }
  2201. LPWSTR
  2202. AnsiToUnicodeStringWithAlloc(
  2203. LPSTR pAnsi
  2204. )
  2205. /*++
  2206. Description:
  2207. Convert ANSI string to UNICODE. Routine allocates memory from the heap
  2208. which should be freed by the caller.
  2209. Arguments:
  2210. pAnsi - Points to the ANSI string
  2211. Return Vlaue:
  2212. Pointer to UNICODE string
  2213. --*/
  2214. {
  2215. LPWSTR pUnicode;
  2216. DWORD rc;
  2217. rc = MultiByteToWideChar(CP_ACP,
  2218. MB_PRECOMPOSED,
  2219. pAnsi,
  2220. -1,
  2221. NULL,
  2222. 0);
  2223. rc *= sizeof(WCHAR);
  2224. if ( !rc || !(pUnicode = (LPWSTR) AllocSplMem(rc)) )
  2225. return NULL;
  2226. rc = MultiByteToWideChar(CP_ACP,
  2227. MB_PRECOMPOSED,
  2228. pAnsi,
  2229. -1,
  2230. pUnicode,
  2231. rc);
  2232. if ( rc )
  2233. return pUnicode;
  2234. else {
  2235. FreeSplMem(pUnicode);
  2236. return NULL;
  2237. }
  2238. }
  2239. VOID
  2240. FreeIniSpoolerOtherNames(
  2241. PINISPOOLER pIniSpooler
  2242. )
  2243. {
  2244. DWORD Index = 0;
  2245. if ( pIniSpooler->ppszOtherNames ) {
  2246. for ( Index = 0 ; Index < pIniSpooler->cOtherNames ; ++Index ) {
  2247. FreeSplMem(pIniSpooler->ppszOtherNames[Index]);
  2248. }
  2249. FreeSplMem(pIniSpooler->ppszOtherNames);
  2250. }
  2251. pIniSpooler->cOtherNames = 0;
  2252. }
  2253. BOOL
  2254. SplMonitorIsInstalled(
  2255. LPWSTR pMonitorName
  2256. )
  2257. {
  2258. BOOL bRet;
  2259. EnterSplSem();
  2260. bRet = FindMonitor(pMonitorName, pLocalIniSpooler) != NULL;
  2261. LeaveSplSem();
  2262. return bRet;
  2263. }
  2264. BOOL
  2265. PrinterDriverEvent(
  2266. PINIPRINTER pIniPrinter,
  2267. INT PrinterEvent,
  2268. LPARAM lParam
  2269. )
  2270. /*++
  2271. --*/
  2272. {
  2273. BOOL ReturnValue = FALSE;
  2274. LPWSTR pPrinterName = NULL;
  2275. BOOL InSpoolSem = TRUE;
  2276. SplOutSem();
  2277. EnterSplSem();
  2278. //
  2279. // We have to Clone the name string, incase someone does a
  2280. // rename whilst outside criticalsection.
  2281. //
  2282. pPrinterName = pszGetPrinterName( pIniPrinter, TRUE, pszLocalsplOnlyToken);
  2283. LeaveSplSem();
  2284. SplOutSem();
  2285. if ( (pIniPrinter->pIniSpooler->SpoolerFlags & SPL_PRINTER_DRIVER_EVENT) &&
  2286. pPrinterName != NULL ) {
  2287. ReturnValue = SplDriverEvent( pPrinterName, PrinterEvent, lParam );
  2288. }
  2289. FreeSplStr( pPrinterName );
  2290. return ReturnValue;
  2291. }
  2292. BOOL
  2293. SplDriverEvent(
  2294. LPWSTR pName,
  2295. INT PrinterEvent,
  2296. LPARAM lParam
  2297. )
  2298. {
  2299. BOOL ReturnValue = FALSE;
  2300. if ( pfnPrinterEvent != NULL ) {
  2301. SplOutSem();
  2302. SPLASSERT( pName && PrinterEvent );
  2303. DBGMSG(DBG_INFO, ("SplDriverEvent %ws %d %x\n", pName, PrinterEvent, lParam));
  2304. ReturnValue = (*pfnPrinterEvent)( pName, PrinterEvent, PRINTER_EVENT_FLAG_NO_UI, lParam );
  2305. }
  2306. return ReturnValue;
  2307. }
  2308. DWORD
  2309. OpenPrinterKey(
  2310. PINIPRINTER pIniPrinter,
  2311. REGSAM samDesired,
  2312. HANDLE *phKey,
  2313. LPCWSTR pKeyName,
  2314. BOOL bOpen
  2315. )
  2316. /*++
  2317. Description: OpenPrinterKey
  2318. Opens "Printers" and IniPrinter->pName keys, then opens
  2319. specified subkey "pKeyName" if pKeyName is not NULL
  2320. This routine needs to be called from inside spooler semaphore
  2321. Arguments:
  2322. pIniPrinter - Points to IniPrinter
  2323. *phPrinterRootKey - Handle to "Printers" key on return
  2324. *phPrinterKey - Handle to IniPrinter->pName key on return
  2325. *hKey - Handle to pKeyName key on return
  2326. pKeyName - Points to SubKey to open
  2327. Return Value:
  2328. Success or failure status
  2329. Author: Steve Wilson (NT)
  2330. --*/
  2331. {
  2332. LPWSTR pThisKeyName = NULL;
  2333. LPWSTR pPrinterKeyName = NULL;
  2334. DWORD rc;
  2335. PINISPOOLER pIniSpooler = pIniPrinter->pIniSpooler;
  2336. SplInSem();
  2337. if (!(pPrinterKeyName = SubChar(pIniPrinter->pName, L'\\', L','))) {
  2338. rc = GetLastError();
  2339. goto error;
  2340. }
  2341. if (pKeyName && *pKeyName){
  2342. pThisKeyName = AllocSplMem(( wcslen(pPrinterKeyName) +
  2343. wcslen(pKeyName) + 2 ) * sizeof(WCHAR) );
  2344. if (!pThisKeyName) {
  2345. rc = GetLastError();
  2346. goto error;
  2347. }
  2348. wsprintf(pThisKeyName, L"%ws\\%ws", pPrinterKeyName, pKeyName);
  2349. } else {
  2350. pThisKeyName = pPrinterKeyName;
  2351. }
  2352. if (bOpen) { // Open
  2353. rc = SplRegOpenKey( pIniSpooler->hckPrinters,
  2354. pThisKeyName,
  2355. samDesired,
  2356. phKey,
  2357. pIniSpooler );
  2358. }
  2359. else { // Create
  2360. rc = SplRegCreateKey( pIniSpooler->hckPrinters,
  2361. pThisKeyName,
  2362. 0,
  2363. samDesired,
  2364. NULL,
  2365. phKey,
  2366. NULL,
  2367. pIniSpooler );
  2368. }
  2369. error:
  2370. if (pThisKeyName != pPrinterKeyName) {
  2371. FreeSplMem(pThisKeyName);
  2372. }
  2373. FreeSplStr(pPrinterKeyName);
  2374. return rc;
  2375. }
  2376. BOOL
  2377. SplGetDriverDir(
  2378. HANDLE hIniSpooler,
  2379. LPWSTR pszDir,
  2380. LPDWORD pcchDir
  2381. )
  2382. {
  2383. DWORD dwSize;
  2384. PINISPOOLER pIniSpooler = (PINISPOOLER)hIniSpooler;
  2385. SPLASSERT(pIniSpooler && pIniSpooler->signature == ISP_SIGNATURE);
  2386. dwSize = *pcchDir;
  2387. *pcchDir = wcslen(pIniSpooler->pDir) + wcslen(szDriverDir) + 2;
  2388. if ( *pcchDir > dwSize ) {
  2389. SetLastError(ERROR_INVALID_PARAMETER);
  2390. return FALSE;
  2391. }
  2392. wsprintf(pszDir, L"%ws\\%ws", pIniSpooler->pDir, szDriverDir);
  2393. return TRUE;
  2394. }
  2395. VOID
  2396. GetRegistryLocation(
  2397. IN HANDLE hKey,
  2398. IN LPCWSTR pszPath,
  2399. OUT PHANDLE phKeyOut,
  2400. OUT LPCWSTR *ppszPathOut
  2401. )
  2402. /*++
  2403. Routine Description:
  2404. Take a registry path and detect whether it should be absolute (rooted
  2405. from HKEY_LOCAL_MACHINE), or if it should be branched off of the
  2406. subkey that is passed in hKey.
  2407. By convention, if it starts with "\" then it is an absolute path.
  2408. Otherwise it is relative off hKey.
  2409. Arguments:
  2410. hKey - Print hKey
  2411. pszPath - Path to parse. If the pszPath starts with a
  2412. backslash, then it is absolute.
  2413. phKeyOut - New key that should be used.
  2414. ppszPathOut - New path that should be used.
  2415. Return Value:
  2416. --*/
  2417. {
  2418. if( pszPath && ( pszPath[0] == TEXT( '\\' ))){
  2419. *phKeyOut = HKEY_LOCAL_MACHINE;
  2420. *ppszPathOut = &pszPath[1];
  2421. return;
  2422. }
  2423. *phKeyOut = hKey;
  2424. *ppszPathOut = pszPath;
  2425. }
  2426. DWORD
  2427. DeletePrinterSubkey(
  2428. PINIPRINTER pIniPrinter,
  2429. PWSTR pKeyName
  2430. )
  2431. {
  2432. HKEY hKey, hPrinterKey;
  2433. DWORD dwError;
  2434. dwError = OpenPrinterKey(pIniPrinter, KEY_WRITE | KEY_READ, &hPrinterKey, NULL, TRUE);
  2435. if (dwError == ERROR_SUCCESS) {
  2436. dwError = OpenPrinterKey( pIniPrinter,
  2437. KEY_WRITE | KEY_READ,
  2438. &hKey,
  2439. pKeyName,
  2440. TRUE);
  2441. if (dwError == ERROR_SUCCESS) {
  2442. dwError = SplDeleteThisKey( hPrinterKey,
  2443. hKey,
  2444. pKeyName,
  2445. FALSE,
  2446. pIniPrinter->pIniSpooler);
  2447. }
  2448. SplRegCloseKey(hPrinterKey, pIniPrinter->pIniSpooler);
  2449. }
  2450. return dwError;
  2451. }
  2452. PWSTR
  2453. FixDelim(
  2454. PCWSTR pszInBuffer,
  2455. WCHAR wcDelim
  2456. )
  2457. /*++
  2458. Routine Description:
  2459. Removes duplicate delimiters from a set of delimited strings
  2460. Arguments:
  2461. pszInBuffer - Input list of comma delimited strings
  2462. wcDelim - The delimit character
  2463. Return Value:
  2464. Returns a fixed string
  2465. --*/
  2466. {
  2467. PWSTR pszIn, pszOut, pszOutBuffer;
  2468. BOOL bFoundDelim = TRUE;
  2469. pszOutBuffer = (PWSTR) AllocSplMem((wcslen(pszInBuffer) + 1)*sizeof(WCHAR));
  2470. if (pszOutBuffer) {
  2471. for(pszOut = pszOutBuffer, pszIn = (PWSTR) pszInBuffer ; *pszIn ; ++pszIn) {
  2472. if (*pszIn == wcDelim) {
  2473. if (!bFoundDelim) {
  2474. bFoundDelim = TRUE;
  2475. *pszOut++ = *pszIn;
  2476. }
  2477. } else {
  2478. bFoundDelim = FALSE;
  2479. *pszOut++ = *pszIn;
  2480. }
  2481. }
  2482. // Check for trailing delimiter
  2483. if (pszOut != pszOutBuffer && *(pszOut - 1) == wcDelim) {
  2484. *(pszOut - 1) = L'\0';
  2485. }
  2486. *pszOut = L'\0';
  2487. }
  2488. return pszOutBuffer;
  2489. }
  2490. PWSTR
  2491. Array2DelimString(
  2492. PSTRINGS pStringArray,
  2493. WCHAR wcDelim
  2494. )
  2495. /*++
  2496. Routine Description:
  2497. Converts a PSTRINGS structure to a set of delimited strings
  2498. Arguments:
  2499. pStringArray - Input PSTRINGS structure
  2500. wcDelim - The delimit character
  2501. Return Value:
  2502. Delimited string buffer
  2503. --*/
  2504. {
  2505. DWORD i, nBytes;
  2506. PWSTR pszDelimString;
  2507. WCHAR szDelimString[2];
  2508. if (!pStringArray || pStringArray->nElements == 0)
  2509. return NULL;
  2510. szDelimString[0] = wcDelim;
  2511. szDelimString[1] = L'\0';
  2512. // Determine memory requirement
  2513. for (i = nBytes = 0 ; i < pStringArray->nElements ; ++i) {
  2514. //
  2515. // allocate extra space for the delimiter
  2516. //
  2517. if (pStringArray->ppszString[i])
  2518. nBytes += (wcslen(pStringArray->ppszString[i]) + 1)*sizeof (WCHAR);
  2519. }
  2520. pszDelimString = (PWSTR) AllocSplMem(nBytes);
  2521. if (!pszDelimString)
  2522. return NULL;
  2523. for (i = 0 ; i < pStringArray->nElements - 1 ; ++i) {
  2524. if (pStringArray->ppszString[i]) {
  2525. wcscat(pszDelimString, pStringArray->ppszString[i]);
  2526. wcscat(pszDelimString, szDelimString);
  2527. }
  2528. }
  2529. if (pStringArray->ppszString[i])
  2530. wcscat(pszDelimString, pStringArray->ppszString[i]);
  2531. return pszDelimString;
  2532. }
  2533. PSTRINGS
  2534. ShortNameArray2LongNameArray(
  2535. PSTRINGS pShortNames
  2536. )
  2537. /*++
  2538. Routine Description:
  2539. Converts a PSTRINGS structure containing short names to a PSTRINGS struct containing the dns
  2540. equivalents
  2541. Arguments:
  2542. pStringArray - Input PSTRINGS structure
  2543. Return Value:
  2544. PSTRINGS struct containing the dns equivalents of the input short name PSTRINGS struct.
  2545. --*/
  2546. {
  2547. PSTRINGS pLongNames;
  2548. DWORD i;
  2549. if (!pShortNames) {
  2550. SetLastError(ERROR_INVALID_PARAMETER);
  2551. return NULL;
  2552. }
  2553. // Allocate LongNameArray
  2554. pLongNames = AllocStringArray(pShortNames->nElements);
  2555. if (!pLongNames)
  2556. return NULL;
  2557. for (i = 0 ; i < pShortNames->nElements ; ++i) {
  2558. // GetDNSMachineName may fail, leaving the LongNameArray element empty. This is okay.
  2559. GetDNSMachineName(pShortNames->ppszString[i], &pLongNames->ppszString[i]);
  2560. }
  2561. pLongNames->nElements = pShortNames->nElements;
  2562. return pLongNames;
  2563. }
  2564. PSTRINGS
  2565. DelimString2Array(
  2566. PCWSTR pszDelimString,
  2567. WCHAR wcDelim
  2568. )
  2569. /*++
  2570. Routine Description:
  2571. Converts a delimited string to a PSTRINGS structure
  2572. Arguments:
  2573. pszDelimString - Input, delimited strings
  2574. wcDelim - The delimit character
  2575. Return Value:
  2576. PSTRINGS structure
  2577. --*/
  2578. {
  2579. PWSTR psz, pszDelim;
  2580. PSTRINGS pStrings = NULL;
  2581. ULONG i, cChar, nStrings;
  2582. // Get number of names
  2583. for (psz = (PWSTR) pszDelimString, nStrings = 0 ; psz++ ; psz = wcschr(psz, wcDelim))
  2584. ++nStrings;
  2585. pStrings = AllocStringArray(nStrings);
  2586. if (!pStrings)
  2587. goto error;
  2588. // Copy delimited string to array
  2589. for (i = 0, psz = (PWSTR) pszDelimString ; i < nStrings && psz ; ++i, psz = pszDelim + 1) {
  2590. pszDelim = wcschr(psz, wcDelim);
  2591. if (pszDelim) {
  2592. cChar = (ULONG) (pszDelim - psz);
  2593. pStrings->ppszString[i] = (PWSTR) AllocSplMem((cChar + 1)*sizeof(WCHAR));
  2594. } else {
  2595. pStrings->ppszString[i] = (PWSTR) AllocSplMem((wcslen(psz) + 1)*sizeof(WCHAR));
  2596. }
  2597. if (!pStrings->ppszString[i]) {
  2598. pStrings->nElements = i;
  2599. FreeStringArray(pStrings);
  2600. pStrings = NULL;
  2601. goto error;
  2602. }
  2603. if (pszDelim) {
  2604. wcsncpy(pStrings->ppszString[i], psz, cChar);
  2605. *(pStrings->ppszString[i] + cChar) = L'\0';
  2606. } else {
  2607. wcscpy(pStrings->ppszString[i], psz);
  2608. }
  2609. }
  2610. pStrings->nElements = nStrings;
  2611. error:
  2612. return pStrings;
  2613. }
  2614. VOID
  2615. FreeStringArray(
  2616. PSTRINGS pString
  2617. )
  2618. /*++
  2619. Routine Description:
  2620. Frees a PSTRINGS structure
  2621. Arguments:
  2622. pString - PSTRINGS structure to free
  2623. Return Value:
  2624. --*/
  2625. {
  2626. DWORD i;
  2627. if (pString) {
  2628. for (i = 0 ; i < pString->nElements ; ++i) {
  2629. if (pString->ppszString[i])
  2630. FreeSplMem(pString->ppszString[i]);
  2631. }
  2632. FreeSplMem(pString);
  2633. }
  2634. }
  2635. PSTRINGS
  2636. AllocStringArray(
  2637. DWORD nStrings
  2638. )
  2639. /*++
  2640. Routine Description:
  2641. Allocates a PSTRINGS structure
  2642. Arguments:
  2643. nStrings - number of strings in the structure
  2644. Return Value:
  2645. pointer to allocated PSTRINGS structure, if any
  2646. --*/
  2647. {
  2648. PSTRINGS pStrings;
  2649. // Allocate the STRINGS struct
  2650. pStrings = (PSTRINGS) AllocSplMem(sizeof(STRINGS) + (nStrings - 1)*sizeof *pStrings->ppszString);
  2651. return pStrings;
  2652. }
  2653. BOOL
  2654. SplDeleteFile(
  2655. LPCTSTR lpFileName
  2656. )
  2657. /*++
  2658. Routine Name
  2659. SplDeleteFile
  2660. Routine Description:
  2661. Removes SFP protection and Deletes a file.
  2662. If the file is protected and I fail to remove protection,
  2663. the user will get warned with a system pop up.
  2664. Arguments:
  2665. lpFileName - file full path requested
  2666. Return Value:
  2667. DeleteFile's return value
  2668. --*/
  2669. {
  2670. HANDLE RpcHandle = INVALID_HANDLE_VALUE;
  2671. RpcHandle = SfcConnectToServer( NULL );
  2672. if( RpcHandle != INVALID_HANDLE_VALUE ){
  2673. SfcFileException( RpcHandle,
  2674. (PWSTR)lpFileName,
  2675. FILE_ACTION_REMOVED
  2676. );
  2677. SfcClose(RpcHandle);
  2678. }
  2679. //
  2680. // SfcFileException might fail with ERROR_FILE_NOT_FOUND because the file is
  2681. // not in the protected file list.That's why I call DeleteFile anyway.
  2682. //
  2683. return DeleteFile( lpFileName );
  2684. }
  2685. BOOL
  2686. SplMoveFileEx(
  2687. LPCTSTR lpExistingFileName,
  2688. LPCTSTR lpNewFileName,
  2689. DWORD dwFlags
  2690. )
  2691. /*++
  2692. Routine Name
  2693. SplMoveFileEx
  2694. Routine Description:
  2695. Removes SFP protection and move a file;
  2696. If the file is protected and I fail to remove protection,
  2697. the user will get warned with a system pop up.
  2698. Arguments:
  2699. lpExistingFileName - pointer to the name of the existing file
  2700. lpNewFileName - pointer to the new name for the file
  2701. dwFlags - flag that specifies how to move file
  2702. Return Value:
  2703. MoveFileEx's return value
  2704. --*/
  2705. {
  2706. HANDLE RpcHandle = INVALID_HANDLE_VALUE;
  2707. RpcHandle = SfcConnectToServer( NULL );
  2708. if( RpcHandle != INVALID_HANDLE_VALUE ){
  2709. SfcFileException( RpcHandle,
  2710. (PWSTR)lpExistingFileName,
  2711. FILE_ACTION_REMOVED
  2712. );
  2713. SfcClose(RpcHandle);
  2714. }
  2715. //
  2716. // SfcFileException might fail with ERROR_FILE_NOT_FOUND because the file is
  2717. // not in the protected file list.That's why I call MoveFileEx anyway.
  2718. //
  2719. return MoveFileEx( lpExistingFileName, lpNewFileName, dwFlags );
  2720. }
  2721. BOOL
  2722. GetSpoolerPolicy(
  2723. PWSTR pszValue,
  2724. PBYTE pData,
  2725. PDWORD pcbData
  2726. )
  2727. {
  2728. HKEY hPrintPublishKey = NULL;
  2729. DWORD dwType, dwData, cbData;
  2730. BOOL bReturn = FALSE;
  2731. // NOTE: This fcn currently only works if type is REG_DWORD. Expand fcn as needed.
  2732. cbData = sizeof(DWORD);
  2733. // Open the registry key
  2734. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  2735. szPrintPublishPolicy,
  2736. 0,
  2737. KEY_READ,
  2738. &hPrintPublishKey) == ERROR_SUCCESS) {
  2739. // Query for the policy bits
  2740. if (RegQueryValueEx(hPrintPublishKey,
  2741. pszValue,
  2742. NULL,
  2743. &dwType,
  2744. (LPBYTE) pData,
  2745. pcbData) == ERROR_SUCCESS)
  2746. bReturn = TRUE;
  2747. RegCloseKey(hPrintPublishKey);
  2748. }
  2749. return bReturn;
  2750. }
  2751. DWORD
  2752. GetDwPolicy(
  2753. PWSTR pszName,
  2754. DWORD dwDefault
  2755. )
  2756. {
  2757. DWORD dwData, cbData = sizeof(DWORD);
  2758. return GetSpoolerPolicy(pszName, (PBYTE) &dwData, &cbData) ? dwData : dwDefault;
  2759. }
  2760. DWORD
  2761. GetDefaultForKMPrintersBlockedPolicy (
  2762. )
  2763. {
  2764. DWORD Default = KM_PRINTERS_ARE_BLOCKED;
  2765. BOOL bIsNTWorkstation;
  2766. NT_PRODUCT_TYPE NtProductType;
  2767. //
  2768. // DEFAULT_KM_PRINTERS_ARE_BLOCKED is "blocked"
  2769. //
  2770. if ( RtlGetNtProductType(&NtProductType) ) {
  2771. bIsNTWorkstation = NtProductType == NtProductWinNt;
  2772. Default = bIsNTWorkstation ?
  2773. WKS_DEFAULT_KM_PRINTERS_ARE_BLOCKED :
  2774. SERVER_DEFAULT_KM_PRINTERS_ARE_BLOCKED;
  2775. }
  2776. return Default;
  2777. }
  2778. DWORD
  2779. GetServerInstallTimeOut(
  2780. )
  2781. {
  2782. HKEY hKey;
  2783. DWORD dwDummy;
  2784. DWORD dwTimeOut = DEFAULT_MAX_TIMEOUT;
  2785. DWORD dwSize = sizeof(dwTimeOut);
  2786. LPCWSTR cszPrintKey = L"SYSTEM\\CurrentControlSet\\Control\\Print";
  2787. LPCWSTR cszTimeOut = L"ServerInstallTimeOut";
  2788. if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, cszPrintKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS )
  2789. {
  2790. if(RegQueryValueEx( hKey, cszTimeOut, 0, &dwDummy, (LPBYTE)&dwTimeOut, &dwSize ) != ERROR_SUCCESS)
  2791. {
  2792. dwTimeOut = DEFAULT_MAX_TIMEOUT;
  2793. }
  2794. RegCloseKey( hKey );
  2795. }
  2796. return dwTimeOut;
  2797. }
  2798. DWORD
  2799. ReadOverlapped( HANDLE hFile,
  2800. LPVOID lpBuffer,
  2801. DWORD nNumberOfBytesToRead,
  2802. LPDWORD lpNumberOfBytesRead )
  2803. {
  2804. DWORD dwLastError = ERROR_SUCCESS;
  2805. if( hFile != INVALID_HANDLE_VALUE )
  2806. {
  2807. OVERLAPPED Ov;
  2808. ZeroMemory( &Ov,sizeof(Ov));
  2809. if ( !(Ov.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) )
  2810. {
  2811. dwLastError = GetLastError();
  2812. goto Cleanup;
  2813. }
  2814. if( !ReadFile( hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, &Ov ) &&
  2815. GetLastError() != ERROR_IO_PENDING )
  2816. {
  2817. dwLastError = GetLastError();
  2818. goto Cleanup;
  2819. }
  2820. if( WaitForSingleObject(Ov.hEvent, gdwServerInstallTimeOut) == WAIT_TIMEOUT )
  2821. {
  2822. CancelIo(hFile);
  2823. WaitForSingleObject(Ov.hEvent, INFINITE);
  2824. }
  2825. if( !GetOverlappedResult(hFile, &Ov, lpNumberOfBytesRead, FALSE) )
  2826. dwLastError = GetLastError();
  2827. Cleanup:
  2828. if ( Ov.hEvent )
  2829. CloseHandle(Ov.hEvent);
  2830. }
  2831. else
  2832. {
  2833. dwLastError = ERROR_INVALID_HANDLE;
  2834. SetLastError( dwLastError );
  2835. }
  2836. return dwLastError;
  2837. }
  2838. BOOL
  2839. WriteOverlapped( HANDLE hFile,
  2840. LPVOID lpBuffer,
  2841. DWORD nNumberOfBytesToRead,
  2842. LPDWORD lpNumberOfBytesRead )
  2843. {
  2844. DWORD dwLastError = ERROR_SUCCESS;
  2845. if( hFile != INVALID_HANDLE_VALUE )
  2846. {
  2847. OVERLAPPED Ov;
  2848. ZeroMemory( &Ov,sizeof(Ov));
  2849. if ( !(Ov.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) )
  2850. {
  2851. dwLastError = GetLastError();
  2852. goto Cleanup;
  2853. }
  2854. if( !WriteFile( hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, &Ov ) &&
  2855. GetLastError() != ERROR_IO_PENDING )
  2856. {
  2857. dwLastError = GetLastError();
  2858. goto Cleanup;
  2859. }
  2860. if( WaitForSingleObject(Ov.hEvent, gdwServerInstallTimeOut) == WAIT_TIMEOUT )
  2861. {
  2862. CancelIo(hFile);
  2863. WaitForSingleObject(Ov.hEvent, INFINITE);
  2864. }
  2865. if( !GetOverlappedResult(hFile, &Ov, lpNumberOfBytesRead, FALSE) )
  2866. dwLastError = GetLastError();
  2867. Cleanup:
  2868. if ( Ov.hEvent )
  2869. CloseHandle(Ov.hEvent);
  2870. }
  2871. else
  2872. {
  2873. dwLastError = ERROR_INVALID_HANDLE;
  2874. SetLastError(dwLastError);
  2875. }
  2876. return (dwLastError == ERROR_SUCCESS);
  2877. }
  2878. ULONG_PTR
  2879. AlignToRegType(
  2880. IN ULONG_PTR Data,
  2881. IN DWORD RegType
  2882. )
  2883. {
  2884. //
  2885. // Alings the value if Data to the boundary
  2886. // dictated by the type of data read from registry.
  2887. //
  2888. ULONG_PTR Boundary;
  2889. switch ( RegType )
  2890. {
  2891. //
  2892. // Binary data could store any kind of data. The pointer is casted
  2893. // to LPDWORD or LPBOOL so make sure it is native aligned.
  2894. //
  2895. case REG_BINARY:
  2896. {
  2897. Boundary = sizeof(ULONG_PTR);
  2898. }
  2899. break;
  2900. case REG_SZ:
  2901. case REG_EXPAND_SZ:
  2902. case REG_MULTI_SZ:
  2903. {
  2904. Boundary = sizeof(WCHAR);
  2905. }
  2906. break;
  2907. case REG_DWORD:
  2908. case REG_DWORD_BIG_ENDIAN:
  2909. {
  2910. Boundary = sizeof(DWORD32);
  2911. }
  2912. break;
  2913. case REG_QWORD:
  2914. {
  2915. Boundary = sizeof(DWORD64);
  2916. }
  2917. break;
  2918. case REG_NONE:
  2919. default:
  2920. {
  2921. Boundary = sizeof(ULONG_PTR);
  2922. }
  2923. }
  2924. return (Data + (Boundary - 1))&~(Boundary - 1);
  2925. }
  2926. /*++
  2927. Routine Name
  2928. BuildAclStruct
  2929. Routine Description:
  2930. Helper function. Builds a vector of ACEs to allow all
  2931. access to administrators and system. The caller has to
  2932. free the pstrName fields
  2933. Arguments:
  2934. cElements - number of elements in the array
  2935. pExplAcc - vector of aces
  2936. Return Value:
  2937. WIN32 error code
  2938. --*/
  2939. DWORD
  2940. BuildAclStruct(
  2941. IN DWORD cElements,
  2942. IN OUT EXPLICIT_ACCESS *pExplAcc
  2943. )
  2944. {
  2945. DWORD dwError = ERROR_INVALID_PARAMETER;
  2946. if (pExplAcc && cElements==2)
  2947. {
  2948. PSID pAdminSid = NULL;
  2949. PSID pSystemSid = NULL;
  2950. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  2951. //
  2952. // Get SID for the built in system account
  2953. //
  2954. dwError = AllocateAndInitializeSid(&NtAuthority,
  2955. 1,
  2956. SECURITY_LOCAL_SYSTEM_RID,
  2957. 0, 0, 0, 0, 0, 0, 0,
  2958. &pSystemSid) &&
  2959. AllocateAndInitializeSid(&NtAuthority,
  2960. 2,
  2961. SECURITY_BUILTIN_DOMAIN_RID,
  2962. DOMAIN_ALIAS_RID_ADMINS,
  2963. 0, 0, 0, 0, 0, 0,
  2964. &pAdminSid) ? ERROR_SUCCESS : GetLastError();
  2965. if (dwError == ERROR_SUCCESS)
  2966. {
  2967. //
  2968. // Initialize the EXPLICIT_ACCESS with information about administrators
  2969. //
  2970. pExplAcc[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
  2971. pExplAcc[0].Trustee.pMultipleTrustee = NULL;
  2972. pExplAcc[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  2973. pExplAcc[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
  2974. pExplAcc[0].Trustee.ptstrName = (PTSTR)pAdminSid;
  2975. pExplAcc[0].grfAccessMode = GRANT_ACCESS;
  2976. pExplAcc[0].grfAccessPermissions = GENERIC_ALL;
  2977. pExplAcc[0].grfInheritance = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
  2978. //
  2979. // Initialize the EXPLICIT_ACCESS with information about the system
  2980. //
  2981. pExplAcc[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
  2982. pExplAcc[1].Trustee.pMultipleTrustee = NULL;
  2983. pExplAcc[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  2984. pExplAcc[1].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
  2985. pExplAcc[1].Trustee.ptstrName = (PTSTR)pSystemSid;
  2986. pExplAcc[1].grfAccessMode = GRANT_ACCESS;
  2987. pExplAcc[1].grfAccessPermissions = GENERIC_ALL;
  2988. pExplAcc[1].grfInheritance = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
  2989. }
  2990. else
  2991. {
  2992. //
  2993. // Note that we never end up here and have pAdminSid not NULL. However, for the
  2994. // sake of consisentcy and extensibility we attempt to clean up both strcutures
  2995. //
  2996. if (pSystemSid) FreeSid(pSystemSid);
  2997. if (pAdminSid) FreeSid(pAdminSid);
  2998. }
  2999. }
  3000. return dwError;
  3001. }
  3002. /*++
  3003. Routine Name
  3004. CreateProtectedDirectory
  3005. Routine Description:
  3006. Creates a directory with full access only to admins and to
  3007. the system. Contained objects inherit these permissions.
  3008. Arguments:
  3009. pszDir - directory name
  3010. Return Value:
  3011. WIN32 error code
  3012. --*/
  3013. DWORD
  3014. CreateProtectedDirectory(
  3015. IN LPCWSTR pszDir
  3016. )
  3017. {
  3018. DWORD dwError = ERROR_INVALID_PARAMETER;
  3019. if (pszDir)
  3020. {
  3021. SECURITY_DESCRIPTOR SecDesc;
  3022. SECURITY_ATTRIBUTES SecAttr;
  3023. PACL pDacl = NULL;
  3024. EXPLICIT_ACCESS ExplicitAccessVector[2] = {0};
  3025. if ((dwError = InitializeSecurityDescriptor(&SecDesc,
  3026. SECURITY_DESCRIPTOR_REVISION) ?
  3027. ERROR_SUCCESS : GetLastError()) == ERROR_SUCCESS &&
  3028. //
  3029. // Initialize the ExplicitAccessVector
  3030. //
  3031. (dwError = BuildAclStruct(COUNTOF(ExplicitAccessVector),
  3032. ExplicitAccessVector)) == ERROR_SUCCESS &&
  3033. //
  3034. // Initialize the DACL
  3035. //
  3036. (dwError = SetEntriesInAcl(COUNTOF(ExplicitAccessVector),
  3037. ExplicitAccessVector,
  3038. NULL,
  3039. &pDacl)) == ERROR_SUCCESS &&
  3040. //
  3041. // Set the DACL in the security descriptor
  3042. //
  3043. (dwError = SetSecurityDescriptorDacl(&SecDesc,
  3044. TRUE,
  3045. pDacl,
  3046. FALSE) ? ERROR_SUCCESS : GetLastError()) == ERROR_SUCCESS &&
  3047. //
  3048. // Check if the security descriptor is valid. Function does not set last error
  3049. //
  3050. (dwError = IsValidSecurityDescriptor(&SecDesc) ?
  3051. ERROR_SUCCESS : ERROR_INVALID_SECURITY_DESCR) == ERROR_SUCCESS)
  3052. {
  3053. //
  3054. // Put the security descriptor in the security attribute
  3055. //
  3056. SecAttr.bInheritHandle = FALSE;
  3057. SecAttr.nLength = sizeof(SecAttr);
  3058. SecAttr.lpSecurityDescriptor = &SecDesc;
  3059. dwError = CreateDirectory(pszDir, &SecAttr) ? ERROR_SUCCESS : GetLastError();
  3060. }
  3061. //
  3062. // The ptstrName here points to a sid obtained via AllocAndInitializeSid
  3063. //
  3064. if (ExplicitAccessVector[0].Trustee.ptstrName)
  3065. {
  3066. FreeSid(ExplicitAccessVector[0].Trustee.ptstrName);
  3067. }
  3068. //
  3069. // The ptstrName here points to a sid obtained via AllocAndInitializeSid
  3070. //
  3071. if (ExplicitAccessVector[1].Trustee.ptstrName)
  3072. {
  3073. FreeSid((PSID)ExplicitAccessVector[1].Trustee.ptstrName);
  3074. }
  3075. }
  3076. DBGMSG(DBG_CLUSTER, ("CreateProtectedDirectory returns Win32 error %u\n", dwError));
  3077. return dwError;
  3078. }
  3079. /*++
  3080. Routine Name
  3081. CopyFileToDirectory
  3082. Routine Description:
  3083. Copies a file to a directory. File is fully qualified.
  3084. The function takes a pszDestDir and up to 3 directories.
  3085. It will create the dir: pszRoot\pszDir1\pszDir2\pszDir3
  3086. and copy the file over there. This is a helper function
  3087. for installing drivers on clusters. The directory strcuture
  3088. is created with special privileges. Only the system and
  3089. administrators have access to it.
  3090. Arguments:
  3091. pssDestDirt - destination directory
  3092. pszDir1 - optional
  3093. pszDir2 - optional
  3094. pszdir3 - optional
  3095. pszFullFileName - qualified file path
  3096. Return Value:
  3097. WIN32 error code
  3098. --*/
  3099. DWORD
  3100. CopyFileToDirectory(
  3101. IN LPCWSTR pszFullFileName,
  3102. IN LPCWSTR pszDestDir,
  3103. IN LPCWSTR pszDir1,
  3104. IN LPCWSTR pszDir2,
  3105. IN LPCWSTR pszDir3
  3106. )
  3107. {
  3108. DWORD dwError = ERROR_INVALID_PARAMETER;
  3109. LPWSTR pszFile = NULL;
  3110. //
  3111. // Our pszfullFileName must contain at least one "\"
  3112. //
  3113. if (pszFullFileName &&
  3114. pszDestDir &&
  3115. (pszFile = wcsrchr(pszFullFileName, L'\\')))
  3116. {
  3117. LPCWSTR ppszArray[] = {pszDir1, pszDir2, pszDir3};
  3118. WCHAR szNewPath[MAX_PATH];
  3119. DWORD uIndex;
  3120. DBGMSG(DBG_CLUSTER, ("CopyFileToDirectory\n\tpszFullFile "TSTR"\n\tpszDest "TSTR"\n\tDir1 "TSTR"\n\tDir2 "TSTR"\n\tDir3 "TSTR"\n",
  3121. pszFullFileName, pszDestDir, pszDir1, pszDir2, pszDir3));
  3122. //
  3123. // Prepare buffer for the loop (initialize to pszDestDir)
  3124. // Create destination root directory, if not existing
  3125. //
  3126. if ((dwError = StrNCatBuff(szNewPath,
  3127. MAX_PATH,
  3128. pszDestDir,
  3129. NULL)) == ERROR_SUCCESS &&
  3130. (DirectoryExists((LPWSTR)pszDestDir) ||
  3131. (dwError = CreateProtectedDirectory(pszDestDir)) == ERROR_SUCCESS))
  3132. {
  3133. for (uIndex = 0;
  3134. uIndex < COUNTOF(ppszArray) && dwError == ERROR_SUCCESS;
  3135. uIndex++)
  3136. {
  3137. //
  3138. // Append the first directory to the path and
  3139. // Create the directory if not existing
  3140. //
  3141. if (ppszArray[uIndex] &&
  3142. (dwError = StrNCatBuff(szNewPath,
  3143. MAX_PATH,
  3144. szNewPath,
  3145. L"\\",
  3146. ppszArray[uIndex],
  3147. NULL)) == ERROR_SUCCESS &&
  3148. !DirectoryExists(szNewPath) &&
  3149. !CreateDirectoryWithoutImpersonatingUser(szNewPath))
  3150. {
  3151. dwError = GetLastError();
  3152. }
  3153. }
  3154. //
  3155. // Create the destination file full name and copy the file
  3156. //
  3157. if (dwError == ERROR_SUCCESS &&
  3158. (dwError = StrNCatBuff(szNewPath,
  3159. MAX_PATH,
  3160. szNewPath,
  3161. pszFile,
  3162. NULL)) == ERROR_SUCCESS)
  3163. {
  3164. dwError = CopyFile(pszFullFileName, szNewPath, FALSE) ? ERROR_SUCCESS : GetLastError();
  3165. }
  3166. DBGMSG(DBG_CLUSTER, ("CopyFileToDirectory szNewPath "TSTR"\n", szNewPath));
  3167. }
  3168. }
  3169. DBGMSG(DBG_CLUSTER, ("CopyFileToDirectory returns Win32 error %u\n", dwError));
  3170. return dwError;
  3171. }
  3172. /*++
  3173. Routine Name
  3174. PropagateMonitorToCluster
  3175. Routine Description:
  3176. For a cluster we keep the following printing resources in the
  3177. cluster data base: drivers, printers, ports, procs. We can also
  3178. have cluster aware port monitors. When the spooler initializes
  3179. those objects, it reads the data from the cluster data base.
  3180. When we write those objects we pass a handle to a key to some
  3181. spooler functions. (Ex WriteDriverIni) The handle is either to the
  3182. local registry or to the cluster database. This procedure is not safe
  3183. with language monitors. LMs keep data in the registry so you need
  3184. to supply a handle to a key in the reg. They don't work with clusters.
  3185. This function will do this:
  3186. 1) write an association key in the cluster data base. We will have
  3187. information like (lm name, dll name) (see below where we store this)
  3188. 2) copy the lm dll to the cluster disk.
  3189. When we fail over we have all it take to install the lm on the local
  3190. node if needed.
  3191. Theoretically this function works for both language and port monitors.
  3192. But it is useless when applied to port monitors.
  3193. Arguments:
  3194. pszName - monitor name
  3195. pszDllName - dll name of the monitor
  3196. pszEnvName - envionment string Ex "Windows NT x86"
  3197. pszEnvDir - the path on disk for the environement Ex w32x86
  3198. pIniSpooler - cluster spooler
  3199. Return Value:
  3200. WIN32 error code
  3201. --*/
  3202. DWORD
  3203. PropagateMonitorToCluster(
  3204. IN LPCWSTR pszName,
  3205. IN LPCWSTR pszDLLName,
  3206. IN LPCWSTR pszEnvName,
  3207. IN LPCWSTR pszEnvDir,
  3208. IN PINISPOOLER pIniSpooler
  3209. )
  3210. {
  3211. DWORD dwError = ERROR_INVALID_PARAMETER;
  3212. HKEY hKeyEnvironments;
  3213. HKEY hKeyCurrentEnv;
  3214. HKEY hKeyMonitors;
  3215. HKEY hKeyCurrentMon;
  3216. SPLASSERT(pIniSpooler->SpoolerFlags & SPL_PRINT && pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER);
  3217. if (pszName && pszDLLName && pszEnvName && pszEnvDir)
  3218. {
  3219. //
  3220. // Check if we added an entry for this lang monitor already
  3221. // The cluster database for the spooler resource looks like:
  3222. //
  3223. // Parameters
  3224. // |
  3225. // +- Environments
  3226. // | |
  3227. // | +- Windows NT x86
  3228. // |
  3229. // +- OtherMonitors
  3230. // |
  3231. // +- Foo
  3232. // | |
  3233. // | +- Driver = Foo.dll
  3234. // |
  3235. // +- Bar
  3236. // |
  3237. // +- Driver = Bar.dll
  3238. //
  3239. if ((dwError = SplRegCreateKey(pIniSpooler->hckRoot,
  3240. ipszClusterDatabaseEnvironments,
  3241. 0,
  3242. KEY_READ | KEY_WRITE,
  3243. NULL,
  3244. &hKeyEnvironments,
  3245. NULL,
  3246. pIniSpooler)) == ERROR_SUCCESS)
  3247. {
  3248. if ((dwError = SplRegCreateKey(hKeyEnvironments,
  3249. pszEnvName,
  3250. 0,
  3251. KEY_READ | KEY_WRITE,
  3252. NULL,
  3253. &hKeyCurrentEnv,
  3254. NULL,
  3255. pIniSpooler)) == ERROR_SUCCESS)
  3256. {
  3257. if ((dwError = SplRegCreateKey(hKeyCurrentEnv,
  3258. szClusterNonAwareMonitors,
  3259. 0,
  3260. KEY_READ | KEY_WRITE,
  3261. NULL,
  3262. &hKeyMonitors,
  3263. NULL,
  3264. pIniSpooler)) == ERROR_SUCCESS)
  3265. {
  3266. if ((dwError = SplRegCreateKey(hKeyMonitors,
  3267. pszName,
  3268. 0,
  3269. KEY_READ | KEY_WRITE,
  3270. NULL,
  3271. &hKeyCurrentMon,
  3272. NULL,
  3273. pIniSpooler)) == ERROR_SUCCESS)
  3274. {
  3275. DWORD cbNeeded = 0;
  3276. //
  3277. // Check if this driver already exists in the database
  3278. //
  3279. if ((dwError=SplRegQueryValue(hKeyCurrentMon,
  3280. L"Driver",
  3281. NULL,
  3282. NULL,
  3283. &cbNeeded,
  3284. pIniSpooler))==ERROR_MORE_DATA)
  3285. {
  3286. DBGMSG(DBG_CLUSTER, ("CopyMonitorToClusterDisks "TSTR" already exists in cluster DB\n", pszName));
  3287. }
  3288. else
  3289. {
  3290. if (RegSetString(hKeyCurrentMon,
  3291. (LPWSTR)L"Driver",
  3292. (LPWSTR)pszDLLName,
  3293. &dwError,
  3294. pIniSpooler))
  3295. {
  3296. //
  3297. // Copy monitor file to cluster disk
  3298. //
  3299. WCHAR szMonitor[MAX_PATH];
  3300. WCHAR szDestDir[MAX_PATH];
  3301. if (GetSystemDirectory(szMonitor, COUNTOF(szMonitor)))
  3302. {
  3303. if ((dwError = StrNCatBuff(szMonitor,
  3304. COUNTOF(szMonitor),
  3305. szMonitor,
  3306. L"\\",
  3307. pszDLLName,
  3308. NULL)) == ERROR_SUCCESS &&
  3309. (dwError = StrNCatBuff(szDestDir,
  3310. COUNTOF(szDestDir),
  3311. pIniSpooler->pszClusResDriveLetter,
  3312. L"\\",
  3313. szClusterDriverRoot,
  3314. NULL)) == ERROR_SUCCESS)
  3315. {
  3316. dwError = CopyFileToDirectory(szMonitor, szDestDir, pszEnvDir, NULL, NULL);
  3317. }
  3318. }
  3319. else
  3320. {
  3321. dwError = GetLastError();
  3322. }
  3323. }
  3324. //
  3325. // If anything failed, delete the entry from the database
  3326. //
  3327. if (dwError != ERROR_SUCCESS)
  3328. {
  3329. dwError = SplRegDeleteKey(hKeyMonitors, pszName, pIniSpooler);
  3330. DBGMSG(DBG_CLUSTER, ("CopyMonitorToClusterDisks Error %u cleaned up cluster DB\n", dwError));
  3331. }
  3332. }
  3333. SplRegCloseKey(hKeyCurrentMon, pIniSpooler);
  3334. }
  3335. SplRegCloseKey(hKeyMonitors, pIniSpooler);
  3336. }
  3337. SplRegCloseKey(hKeyCurrentEnv, pIniSpooler);
  3338. }
  3339. SplRegCloseKey(hKeyEnvironments, pIniSpooler);
  3340. }
  3341. }
  3342. DBGMSG(DBG_CLUSTER, ("PropagateMonitorToCluster returns Win32 error %u\n", dwError));
  3343. return dwError;
  3344. }
  3345. /*++
  3346. Routine Name
  3347. InstallMonitorFromCluster
  3348. Routine Description:
  3349. For a cluster we keep the following printing resources in the
  3350. cluster data base: drivers, printers, ports, procs. We can also
  3351. have cluster aware port monitors. When the spooler initializes
  3352. those objects, it reads the data from the cluster data base.
  3353. When we write those objects we pass a handle to a key to some
  3354. spooler functions. (Ex WriteDriverIni) The handle is either to the
  3355. local registry or to the cluster database. This procedure is not safe
  3356. with language monitors. LMs keep data in the registry so you need
  3357. to supply a handle to a key in the reg. They don't work with clusters.
  3358. This function will do this:
  3359. 1) read an association key in the cluster data base. We will have
  3360. information like (lm name, dll name) (see below where we store this)
  3361. 2) copy the lm dll from the cluster disk to the local disk.
  3362. 3) install the monitor with the local spooler
  3363. Theoretically this function works for both language and port monitors.
  3364. But it is useless when applied to port monitors.
  3365. Arguments:
  3366. pszName - monitor name
  3367. pszEnvName - envionment string Ex "Windows NT x86"
  3368. pszEnvDir - the path on disk for the environement Ex w32x86
  3369. pIniSpooler - cluster spooler
  3370. Return Value:
  3371. WIN32 error code
  3372. --*/
  3373. DWORD
  3374. InstallMonitorFromCluster(
  3375. IN LPCWSTR pszName,
  3376. IN LPCWSTR pszEnvName,
  3377. IN LPCWSTR pszEnvDir,
  3378. IN PINISPOOLER pIniSpooler
  3379. )
  3380. {
  3381. DWORD dwError = ERROR_INVALID_PARAMETER;
  3382. HKEY hKeyParent;
  3383. HKEY hKeyMon;
  3384. SPLASSERT(pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER);
  3385. if (pszName && pszEnvName && pszEnvDir)
  3386. {
  3387. HKEY hKeyEnvironments;
  3388. HKEY hKeyCurrentEnv;
  3389. HKEY hKeyMonitors;
  3390. HKEY hKeyCurrentMon;
  3391. //
  3392. // Check if we added an entry for this lang monitor already
  3393. // The cluster database for the spooler resource looks like:
  3394. //
  3395. // Parameters
  3396. // |
  3397. // +- Environments
  3398. // | |
  3399. // | +- Windows NT x86
  3400. // |
  3401. // +- OtherMonitors
  3402. // |
  3403. // +- Foo
  3404. // | |
  3405. // | +- Driver = Foo.dll
  3406. // |
  3407. // +- Bar
  3408. // |
  3409. // +- Driver = Bar.dll
  3410. //
  3411. if ((dwError = SplRegOpenKey(pIniSpooler->hckRoot,
  3412. ipszClusterDatabaseEnvironments,
  3413. KEY_READ,
  3414. &hKeyEnvironments,
  3415. pIniSpooler)) == ERROR_SUCCESS)
  3416. {
  3417. if ((dwError = SplRegOpenKey(hKeyEnvironments,
  3418. pszEnvName,
  3419. KEY_READ,
  3420. &hKeyCurrentEnv,
  3421. pIniSpooler)) == ERROR_SUCCESS)
  3422. {
  3423. if ((dwError = SplRegOpenKey(hKeyCurrentEnv,
  3424. szClusterNonAwareMonitors,
  3425. KEY_READ,
  3426. &hKeyMonitors,
  3427. pIniSpooler)) == ERROR_SUCCESS)
  3428. {
  3429. if ((dwError = SplRegOpenKey(hKeyMonitors,
  3430. pszName,
  3431. KEY_READ,
  3432. &hKeyCurrentMon,
  3433. pIniSpooler)) == ERROR_SUCCESS)
  3434. {
  3435. LPWSTR pszDLLName = NULL;
  3436. DWORD cchDLLName = 0;
  3437. if (RegGetString(hKeyCurrentMon,
  3438. (LPWSTR)L"Driver",
  3439. &pszDLLName,
  3440. &cchDLLName,
  3441. &dwError,
  3442. TRUE,
  3443. pIniSpooler))
  3444. {
  3445. //
  3446. // We found the monitor entry in the cluster DB
  3447. //
  3448. WCHAR szSource[MAX_PATH];
  3449. WCHAR szDest[MAX_PATH];
  3450. if (GetSystemDirectory(szDest, COUNTOF(szDest)))
  3451. {
  3452. if ((dwError = StrNCatBuff(szDest,
  3453. COUNTOF(szDest),
  3454. szDest,
  3455. L"\\",
  3456. pszDLLName,
  3457. NULL)) == ERROR_SUCCESS &&
  3458. (dwError = StrNCatBuff(szSource,
  3459. COUNTOF(szSource),
  3460. pIniSpooler->pszClusResDriveLetter,
  3461. L"\\",
  3462. szClusterDriverRoot,
  3463. L"\\",
  3464. pszEnvDir,
  3465. L"\\",
  3466. pszDLLName,
  3467. NULL)) == ERROR_SUCCESS)
  3468. {
  3469. //
  3470. // Copy file from K:\PrinterDrivers\W32x86\foo.dll to
  3471. // WINDIR\system32\foo.dll
  3472. //
  3473. if (CopyFile(szSource, szDest, FALSE))
  3474. {
  3475. MONITOR_INFO_2 Monitor;
  3476. Monitor.pDLLName = pszDLLName;
  3477. Monitor.pName = (LPWSTR)pszName;
  3478. Monitor.pEnvironment = (LPWSTR)pszEnvName;
  3479. DBGMSG(DBG_CLUSTER, ("InstallMonitorFromCluster "TSTR" copied\n", pszDLLName));
  3480. //
  3481. // Call AddMonitor to the local spooler
  3482. //
  3483. if (!SplAddMonitor(NULL, 2, (LPBYTE)&Monitor, pLocalIniSpooler))
  3484. {
  3485. dwError = GetLastError();
  3486. }
  3487. }
  3488. else
  3489. {
  3490. dwError = GetLastError();
  3491. }
  3492. }
  3493. }
  3494. else
  3495. {
  3496. dwError = GetLastError();
  3497. }
  3498. }
  3499. SplRegCloseKey(hKeyCurrentMon, pIniSpooler);
  3500. }
  3501. SplRegCloseKey(hKeyMonitors, pIniSpooler);
  3502. }
  3503. SplRegCloseKey(hKeyCurrentEnv, pIniSpooler);
  3504. }
  3505. SplRegCloseKey(hKeyEnvironments, pIniSpooler);
  3506. }
  3507. }
  3508. DBGMSG(DBG_CLUSTER, ("InstallMonitorFromCluster returns Win32 error %u\n", dwError));
  3509. return dwError;
  3510. }
  3511. /*++
  3512. Routine Name
  3513. CopyNewerOrOlderFiles
  3514. Routine Description:
  3515. Copies all newer or older files from the source directory to the dest dir.
  3516. If you supply a bool function that takes 2 parameters, it will apply that
  3517. function for each copied file. func(NULL, file.) This is useful when I have
  3518. to caopy over ICM profiles. Then I can resuse this function and have it
  3519. install those profiles as it copies them.
  3520. Arguments:
  3521. pszSourceDir - source directory string
  3522. pszDestDir - destination directory string
  3523. pfn - optional functin to be applied on each copied file
  3524. Return Value:
  3525. WIN32 error code
  3526. --*/
  3527. DWORD
  3528. CopyNewerOrOlderFiles(
  3529. IN LPCWSTR pszSourceDir,
  3530. IN LPCWSTR pszDestDir,
  3531. IN BOOL (WINAPI *pfn)(LPWSTR, LPWSTR)
  3532. )
  3533. {
  3534. DWORD dwError = ERROR_INVALID_PARAMETER;
  3535. if (pszSourceDir && pszDestDir)
  3536. {
  3537. WCHAR szSearchPath[MAX_PATH];
  3538. //
  3539. // Build the search path. We look for all files
  3540. //
  3541. if ((dwError = StrNCatBuff(szSearchPath,
  3542. COUNTOF(szSearchPath),
  3543. pszSourceDir,
  3544. L"\\*",
  3545. NULL)) == ERROR_SUCCESS)
  3546. {
  3547. WIN32_FIND_DATA SourceFindData;
  3548. HANDLE hSourceFind;
  3549. //
  3550. // Find first file that meets the criteria
  3551. //
  3552. if ((hSourceFind = FindFirstFile(szSearchPath, &SourceFindData)) != INVALID_HANDLE_VALUE)
  3553. {
  3554. do
  3555. {
  3556. WCHAR szMasterPath[MAX_PATH];
  3557. //
  3558. // Search for the rest of the files. We are interested in files that are not directories
  3559. //
  3560. if (!(SourceFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  3561. (dwError = StrNCatBuff(szMasterPath,
  3562. COUNTOF(szMasterPath),
  3563. pszDestDir,
  3564. L"\\",
  3565. SourceFindData.cFileName,
  3566. NULL)) == ERROR_SUCCESS)
  3567. {
  3568. WIN32_FIND_DATA MasterFindData;
  3569. HANDLE hMasterFind;
  3570. BOOL bCopyFile = TRUE;
  3571. WCHAR szFile[MAX_PATH];
  3572. //
  3573. // Check if the file found in source dir exists in the dest dir
  3574. //
  3575. if ((hMasterFind = FindFirstFile(szMasterPath, &MasterFindData)) != INVALID_HANDLE_VALUE)
  3576. {
  3577. //
  3578. // Do not copy file if source and dest have same time stamp
  3579. //
  3580. if (!CompareFileTime(&SourceFindData.ftLastWriteTime, &MasterFindData.ftLastWriteTime))
  3581. {
  3582. bCopyFile = FALSE;
  3583. }
  3584. FindClose(hMasterFind);
  3585. }
  3586. //
  3587. // File either not found in dest dir, or it has a different timp stamp
  3588. //
  3589. if (bCopyFile &&
  3590. (dwError = StrNCatBuff(szFile,
  3591. COUNTOF(szFile),
  3592. pszSourceDir,
  3593. L"\\",
  3594. SourceFindData.cFileName,
  3595. NULL)) == ERROR_SUCCESS &&
  3596. (dwError = CopyFile(szFile,
  3597. szMasterPath,
  3598. FALSE) ? ERROR_SUCCESS : GetLastError()) == ERROR_SUCCESS &&
  3599. pfn)
  3600. {
  3601. dwError = (*pfn)(NULL, szFile) ? ERROR_SUCCESS : GetLastError();
  3602. }
  3603. }
  3604. } while (dwError == ERROR_SUCCESS && FindNextFile(hSourceFind, &SourceFindData));
  3605. FindClose(hSourceFind);
  3606. }
  3607. else if ((dwError = GetLastError()) == ERROR_PATH_NOT_FOUND || dwError == ERROR_FILE_NOT_FOUND)
  3608. {
  3609. //
  3610. // No directory or files, success
  3611. //
  3612. dwError = ERROR_SUCCESS;
  3613. }
  3614. }
  3615. }
  3616. DBGMSG(DBG_CLUSTER, ("CopyNewerOrOlderFiles returns Win32 error %u\n", dwError));
  3617. return dwError;
  3618. }
  3619. /*++
  3620. Routine Name
  3621. CopyICMFromClusterDiskToLocalDisk
  3622. Routine Description:
  3623. Copies all newer or older files from the source directory to the destination
  3624. directory. The source directory is the ICM directory on the cluster disk
  3625. and the destination is the ICM directory on the local machine for the
  3626. cluster spooler. This function will also install the icm profiles with the
  3627. local machine/
  3628. Arguments:
  3629. pIniSpooler - pointer to cluster spooler structure
  3630. Return Value:
  3631. WIN32 error code
  3632. --*/
  3633. DWORD
  3634. CopyICMFromClusterDiskToLocalDisk(
  3635. IN PINISPOOLER pIniSpooler
  3636. )
  3637. {
  3638. DWORD dwError = ERROR_SUCCESS;
  3639. WCHAR szSource[MAX_PATH];
  3640. WCHAR szDest[MAX_PATH];
  3641. SPLASSERT(pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER);
  3642. if ((dwError = StrNCatBuff(szDest,
  3643. COUNTOF(szDest),
  3644. pIniSpooler->pDir,
  3645. L"\\Drivers\\Color",
  3646. NULL)) == ERROR_SUCCESS &&
  3647. (dwError = StrNCatBuff(szSource,
  3648. COUNTOF(szSource),
  3649. pIniSpooler->pszClusResDriveLetter,
  3650. L"\\",
  3651. szClusterDriverRoot,
  3652. L"\\",
  3653. L"Color",
  3654. NULL)) == ERROR_SUCCESS)
  3655. {
  3656. HMODULE hLib;
  3657. typedef BOOL (WINAPI *PFN)(LPWSTR, LPWSTR);
  3658. PFN pfn;
  3659. //
  3660. // Make sure the directory on the local disk exists. We will copy files
  3661. // from K:\Printerdrivers\Color to
  3662. // WINDIR\system32\spool\drivers\clus-spl-guid\drivers\color
  3663. //
  3664. CreateCompleteDirectory(szDest);
  3665. if ((hLib = LoadLibrary(L"mscms.dll")) &&
  3666. (pfn = (PFN)GetProcAddress(hLib, "InstallColorProfileW")))
  3667. {
  3668. dwError = CopyNewerOrOlderFiles(szSource, szDest, pfn);
  3669. }
  3670. else
  3671. {
  3672. dwError = GetLastError();
  3673. }
  3674. if (hLib)
  3675. {
  3676. FreeLibrary(hLib);
  3677. }
  3678. DBGMSG(DBG_CLUSTER, ("CopyICMFromClusterDiskToLocalDisk "TSTR" "TSTR" Error %u\n", szSource, szDest, dwError));
  3679. }
  3680. return dwError;
  3681. }
  3682. /*++
  3683. Routine Name
  3684. CopyICMFromLocalDiskToClusterDisk
  3685. Routine Description:
  3686. Copies all newer or older files from the source directory to the destination
  3687. directory. The source directory is the ICM directory on the local machine for
  3688. the cluster spooler. The destination is the ICM directory on the cluster disk
  3689. Arguments:
  3690. pIniSpooler - pointer to cluster spooler structure
  3691. Return Value:
  3692. WIN32 error code
  3693. --*/
  3694. DWORD
  3695. CopyICMFromLocalDiskToClusterDisk(
  3696. IN PINISPOOLER pIniSpooler
  3697. )
  3698. {
  3699. DWORD dwError = ERROR_SUCCESS;
  3700. WCHAR szSource[MAX_PATH];
  3701. WCHAR szDest[MAX_PATH];
  3702. SPLASSERT(pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER);
  3703. if ((dwError = StrNCatBuff(szSource,
  3704. COUNTOF(szSource),
  3705. pIniSpooler->pDir,
  3706. L"\\Drivers\\Color",
  3707. NULL)) == ERROR_SUCCESS &&
  3708. (dwError = StrNCatBuff(szDest,
  3709. COUNTOF(szDest),
  3710. pIniSpooler->pszClusResDriveLetter,
  3711. L"\\",
  3712. szClusterDriverRoot,
  3713. L"\\",
  3714. L"Color",
  3715. NULL)) == ERROR_SUCCESS &&
  3716. //
  3717. // Make sure the destination on the cluster disk exists. We need to create
  3718. // it with special access rights. (only admins and system can read/write)
  3719. //
  3720. ((dwError = CreateProtectedDirectory(szDest)) == ERROR_SUCCESS ||
  3721. dwError == ERROR_ALREADY_EXISTS))
  3722. {
  3723. dwError = CopyNewerOrOlderFiles(szSource, szDest, NULL);
  3724. DBGMSG(DBG_CLUSTER, ("CopyICMFromLocalDiskToClusterDisk "TSTR" "TSTR" Error %u\n", szSource, szDest, dwError));
  3725. }
  3726. return dwError;
  3727. }
  3728. /*++
  3729. Routine Name
  3730. CreateClusterSpoolerEnvironmentsStructure
  3731. Routine Description:
  3732. A pIniSpooler needs a list of all possible pIniEnvironemnts. For the local
  3733. spooler the setup creates the necessary environments keys in the registry under
  3734. HKLM\System\CCS\Control\Print\Environments. We need to propagate the same
  3735. structure for each cluster spooler in the cluster data base.
  3736. Relies on the fact that pLocalIniSpooler is initialized fisrt (among all
  3737. pinispoolers)
  3738. This function builds the following strucutre in the cluster database
  3739. Parameters
  3740. |
  3741. +- Environments
  3742. | |
  3743. | +- Windows NT x86
  3744. | | (Directory = w32x86)
  3745. | |
  3746. | +- Windows 4.0
  3747. | | (Directory = win40)
  3748. | |
  3749. Arguments:
  3750. pIniSpooler - pointer to cluster spooler structure
  3751. Return Value:
  3752. WIN32 error code
  3753. --*/
  3754. DWORD
  3755. CreateClusterSpoolerEnvironmentsStructure(
  3756. IN PINISPOOLER pIniSpooler
  3757. )
  3758. {
  3759. HKEY hEnvironmentsKey;
  3760. DWORD dwError;
  3761. SPLASSERT(pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER);
  3762. if ((dwError = SplRegCreateKey(pIniSpooler->hckRoot,
  3763. pIniSpooler->pszRegistryEnvironments,
  3764. 0,
  3765. KEY_ALL_ACCESS,
  3766. NULL,
  3767. &hEnvironmentsKey,
  3768. NULL,
  3769. pIniSpooler)) == ERROR_SUCCESS)
  3770. {
  3771. PINIENVIRONMENT pIniEnvironment;
  3772. //
  3773. // Loop through the environments of the local spooler
  3774. //
  3775. for (pIniEnvironment = pLocalIniSpooler->pIniEnvironment;
  3776. pIniEnvironment && dwError == ERROR_SUCCESS;
  3777. pIniEnvironment = pIniEnvironment->pNext)
  3778. {
  3779. HKEY hKeyCurrentEnv;
  3780. if ((dwError = SplRegCreateKey(hEnvironmentsKey,
  3781. pIniEnvironment->pName,
  3782. 0,
  3783. KEY_ALL_ACCESS,
  3784. NULL,
  3785. &hKeyCurrentEnv,
  3786. NULL,
  3787. pIniSpooler)) == ERROR_SUCCESS)
  3788. {
  3789. HKEY hKeyPrtProc;
  3790. //
  3791. // Set the value Directory (ex. = W32X86) and create
  3792. // the print processor key
  3793. //
  3794. if (RegSetString(hKeyCurrentEnv,
  3795. szDirectory,
  3796. pIniEnvironment->pDirectory,
  3797. &dwError,
  3798. pIniSpooler) &&
  3799. (dwError = SplRegCreateKey(hKeyCurrentEnv,
  3800. szPrintProcKey,
  3801. 0,
  3802. KEY_ALL_ACCESS,
  3803. NULL,
  3804. &hKeyPrtProc,
  3805. NULL,
  3806. pIniSpooler)) == ERROR_SUCCESS)
  3807. {
  3808. SplRegCloseKey(hKeyPrtProc, pIniSpooler);
  3809. }
  3810. SplRegCloseKey(hKeyCurrentEnv, pIniSpooler);
  3811. }
  3812. }
  3813. SplRegCloseKey(hEnvironmentsKey, pIniSpooler);
  3814. }
  3815. DBGMSG(DBG_CLUSTER, ("CreateClusterSpoolerEnvironmentsStructure returns dwError %u\n\n", dwError));
  3816. return dwError;
  3817. }
  3818. /*++
  3819. Routine Name
  3820. AddLocalDriverToClusterSpooler
  3821. Routine Description:
  3822. Adds a driver that exists on the local inispooler to the cluster spooler.
  3823. It will also add all versions of that driver. We use this function in the
  3824. upgrade scenario. For a certain printer, we need to propage the driver
  3825. (and all the versions of that driver) to the cluster data base and cluster
  3826. disk.
  3827. Arguments:
  3828. pIniSpooler - pointer to cluster spooler structure
  3829. Return Value:
  3830. WIN32 error code
  3831. --*/
  3832. DWORD
  3833. AddLocalDriverToClusterSpooler(
  3834. IN LPCWSTR pszDriver,
  3835. IN PINISPOOLER pIniSpooler
  3836. )
  3837. {
  3838. DWORD dwError = ERROR_SUCCESS;
  3839. PINIENVIRONMENT pIniEnv;
  3840. PINIVERSION pIniVer;
  3841. SplInSem();
  3842. //
  3843. // Traverse all environments and versions
  3844. //
  3845. for (pIniEnv=pLocalIniSpooler->pIniEnvironment; pIniEnv; pIniEnv=pIniEnv->pNext)
  3846. {
  3847. for (pIniVer=pIniEnv->pIniVersion; pIniVer; pIniVer=pIniVer->pNext)
  3848. {
  3849. PINIDRIVER pIniDriver = (PINIDRIVER)FindIniKey((PINIENTRY)pIniVer->pIniDriver, (LPWSTR)pszDriver);
  3850. if (pIniDriver)
  3851. {
  3852. DRIVER_INFO_6 DriverInfo = {0};
  3853. WCHAR szDriverFile[MAX_PATH];
  3854. WCHAR szDataFile[MAX_PATH];
  3855. WCHAR szConfigFile[MAX_PATH];
  3856. WCHAR szHelpFile[MAX_PATH];
  3857. WCHAR szPrefix[MAX_PATH];
  3858. LPWSTR pszzDependentFiles = NULL;
  3859. //
  3860. // Get fully qualified driver file paths. We will call add printer
  3861. // driver without using the scratch directory
  3862. //
  3863. if ((dwError = StrNCatBuff(szDriverFile,
  3864. COUNTOF(szDriverFile),
  3865. pLocalIniSpooler->pDir,
  3866. szDriversDirectory, L"\\",
  3867. pIniEnv->pDirectory, L"\\",
  3868. pIniVer->szDirectory, L"\\",
  3869. pIniDriver->pDriverFile,
  3870. NULL)) == ERROR_SUCCESS &&
  3871. (dwError = StrNCatBuff(szDataFile,
  3872. COUNTOF(szDataFile),
  3873. pLocalIniSpooler->pDir,
  3874. szDriversDirectory, L"\\",
  3875. pIniEnv->pDirectory, L"\\",
  3876. pIniVer->szDirectory, L"\\",
  3877. pIniDriver->pDataFile,
  3878. NULL)) == ERROR_SUCCESS &&
  3879. (dwError = StrNCatBuff(szConfigFile,
  3880. COUNTOF(szConfigFile),
  3881. pLocalIniSpooler->pDir,
  3882. szDriversDirectory, L"\\",
  3883. pIniEnv->pDirectory, L"\\",
  3884. pIniVer->szDirectory, L"\\",
  3885. pIniDriver->pConfigFile,
  3886. NULL)) == ERROR_SUCCESS &&
  3887. (dwError = StrNCatBuff(szHelpFile,
  3888. COUNTOF(szHelpFile),
  3889. pLocalIniSpooler->pDir,
  3890. szDriversDirectory, L"\\",
  3891. pIniEnv->pDirectory, L"\\",
  3892. pIniVer->szDirectory, L"\\",
  3893. pIniDriver->pHelpFile,
  3894. NULL)) == ERROR_SUCCESS &&
  3895. (dwError = StrNCatBuff(szPrefix,
  3896. COUNTOF(szPrefix),
  3897. pLocalIniSpooler->pDir,
  3898. szDriversDirectory, L"\\",
  3899. pIniEnv->pDirectory, L"\\",
  3900. pIniVer->szDirectory, L"\\",
  3901. NULL)) == ERROR_SUCCESS &&
  3902. (dwError = StrCatPrefixMsz(szPrefix,
  3903. pIniDriver->pDependentFiles,
  3904. &pszzDependentFiles)) == ERROR_SUCCESS)
  3905. {
  3906. DBGMSG(DBG_CLUSTER, ("AddLocalDrv szDriverFile "TSTR"\n", szDriverFile));
  3907. DBGMSG(DBG_CLUSTER, ("AddLocalDrv szDataFile "TSTR"\n", szDataFile));
  3908. DBGMSG(DBG_CLUSTER, ("AddLocalDrv szConfigFile "TSTR"\n", szConfigFile));
  3909. DBGMSG(DBG_CLUSTER, ("AddLocalDrv szHelpFile "TSTR"\n", szHelpFile));
  3910. DBGMSG(DBG_CLUSTER, ("AddLocalDrv szPrefix "TSTR"\n", szPrefix));
  3911. DriverInfo.pDriverPath = szDriverFile;
  3912. DriverInfo.pName = pIniDriver->pName;
  3913. DriverInfo.pEnvironment = pIniEnv->pName;
  3914. DriverInfo.pDataFile = szDataFile;
  3915. DriverInfo.pConfigFile = szConfigFile;
  3916. DriverInfo.cVersion = pIniDriver->cVersion;
  3917. DriverInfo.pHelpFile = szHelpFile;
  3918. DriverInfo.pMonitorName = pIniDriver->pMonitorName;
  3919. DriverInfo.pDefaultDataType = pIniDriver->pDefaultDataType;
  3920. DriverInfo.pDependentFiles = pszzDependentFiles;
  3921. DriverInfo.pszzPreviousNames = pIniDriver->pszzPreviousNames;
  3922. DriverInfo.pszMfgName = pIniDriver->pszMfgName;
  3923. DriverInfo.pszOEMUrl = pIniDriver->pszOEMUrl;
  3924. DriverInfo.pszHardwareID = pIniDriver->pszHardwareID;
  3925. DriverInfo.pszProvider = pIniDriver->pszProvider;
  3926. DriverInfo.dwlDriverVersion = pIniDriver->dwlDriverVersion;
  3927. DriverInfo.ftDriverDate = pIniDriver->ftDriverDate;
  3928. LeaveSplSem();
  3929. if (!SplAddPrinterDriverEx(NULL,
  3930. 6,
  3931. (LPBYTE)&DriverInfo,
  3932. APD_COPY_NEW_FILES,
  3933. pIniSpooler,
  3934. FALSE,
  3935. DO_NOT_IMPERSONATE_USER))
  3936. {
  3937. dwError = GetLastError();
  3938. }
  3939. EnterSplSem();
  3940. }
  3941. FreeSplMem(pszzDependentFiles);
  3942. DBGMSG(DBG_CLUSTER, ("AddLocalDrv Env "TSTR" Ver "TSTR" Name "TSTR" Error %u\n",
  3943. pIniEnv->pName, pIniVer->pName, pszDriver, dwError));
  3944. }
  3945. }
  3946. }
  3947. return dwError;
  3948. }
  3949. /*++
  3950. Routine Name
  3951. StrCatPerfixMsz
  3952. Routine Description:
  3953. Take a prefix which is a string Ex "C:\windows\" and a multi sz Ex: "a0b00"
  3954. It will create: c:\windows\a0c:\windows\b00
  3955. The prefix must have a trailing "\".
  3956. Arguments:
  3957. pszPrefix - string to prefix all the strings in the msz
  3958. pszzFiles - msz of files
  3959. ppszzFullPathFiles - out param
  3960. Return Value:
  3961. WIN32 error code
  3962. --*/
  3963. DWORD
  3964. StrCatPrefixMsz(
  3965. IN LPCWSTR pszPrefix,
  3966. IN LPWSTR pszzFiles,
  3967. OUT LPWSTR *ppszzFullPathFiles
  3968. )
  3969. {
  3970. DWORD dwError = ERROR_INVALID_PARAMETER;
  3971. if (pszPrefix && ppszzFullPathFiles)
  3972. {
  3973. *ppszzFullPathFiles = NULL;
  3974. if (pszzFiles)
  3975. {
  3976. WCHAR szNewPath[MAX_PATH] = {0};
  3977. LPWSTR psz;
  3978. LPWSTR pszReturn;
  3979. LPWSTR pszTemp;
  3980. DWORD cbNeeded = 0;
  3981. DWORD dwPrifxLen = wcslen(pszPrefix);
  3982. //
  3983. // We count the number of character of the string
  3984. // that we need to allocate
  3985. //
  3986. for (psz = pszzFiles; psz && *psz;)
  3987. {
  3988. DWORD dwLen = wcslen(psz);
  3989. cbNeeded += dwPrifxLen + dwLen + 1;
  3990. psz += dwLen + 1;
  3991. }
  3992. //
  3993. // Final \0 of the multi sz
  3994. //
  3995. cbNeeded++;
  3996. //
  3997. // Convert to number of bytes
  3998. //
  3999. cbNeeded *= sizeof(WCHAR);
  4000. if (pszReturn = AllocSplMem(cbNeeded))
  4001. {
  4002. for (psz = pszzFiles, pszTemp = pszReturn; psz && *psz; )
  4003. {
  4004. wcscpy(pszTemp, pszPrefix);
  4005. wcscat(pszTemp, psz);
  4006. pszTemp += wcslen(pszTemp);
  4007. *pszTemp = L'\0';
  4008. pszTemp++;
  4009. psz += wcslen(psz) + 1;
  4010. }
  4011. pszTemp = L'\0';
  4012. //
  4013. // Set out param
  4014. //
  4015. *ppszzFullPathFiles = pszReturn;
  4016. dwError = ERROR_SUCCESS;
  4017. }
  4018. else
  4019. {
  4020. dwError = GetLastError();
  4021. }
  4022. }
  4023. else
  4024. {
  4025. //
  4026. // NULL input multi sz, nothing to do
  4027. //
  4028. dwError = ERROR_SUCCESS;
  4029. }
  4030. }
  4031. DBGMSG(DBG_CLUSTER, ("StrCatPerfixMsz returns %u\n", dwError));
  4032. return dwError;
  4033. }
  4034. /*++
  4035. Routine Name
  4036. ClusterSplReadUpgradeKey
  4037. Routine Description:
  4038. After the first reboot following an upgrade of a node, the cluster
  4039. service informs the resdll that a version change occured. At this
  4040. time out spooler resource may be running on another node or may
  4041. not be actie at all. Thus the resdll writes a value in the local
  4042. registry. The vaue name is the GUID for the spooler resource, the
  4043. value is DWORD 1. When the cluster spooler resource fails over on this
  4044. machine it (i.e. now) it queries for that value to know if it needs
  4045. to preform post upgrade operations, like upgrading the printer drivers.
  4046. Arguments:
  4047. pszResource - string respresenation of GUID for the cluster resource
  4048. pdwVlaue - will contain the value in the registry for the GUID
  4049. Return Value:
  4050. WIN32 error code
  4051. --*/
  4052. DWORD
  4053. ClusterSplReadUpgradeKey(
  4054. IN LPCWSTR pszResourceID,
  4055. OUT LPDWORD pdwValue
  4056. )
  4057. {
  4058. DWORD dwError = ERROR_INVALID_PARAMETER;
  4059. HKEY hkRoot = NULL;
  4060. HKEY hkUpgrade = NULL;
  4061. if (pszResourceID && pdwValue)
  4062. {
  4063. *pdwValue = 0;
  4064. if ((dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  4065. SPLREG_CLUSTER_LOCAL_ROOT_KEY,
  4066. 0,
  4067. KEY_READ,
  4068. &hkRoot)) == ERROR_SUCCESS &&
  4069. (dwError = RegOpenKeyEx(hkRoot,
  4070. SPLREG_CLUSTER_UPGRADE_KEY,
  4071. 0,
  4072. KEY_READ,
  4073. &hkUpgrade)) == ERROR_SUCCESS)
  4074. {
  4075. DWORD cbNeeded = sizeof(DWORD);
  4076. dwError = RegQueryValueEx(hkUpgrade, pszResourceID, NULL, NULL, (LPBYTE)pdwValue, &cbNeeded);
  4077. }
  4078. if (hkUpgrade) RegCloseKey(hkUpgrade);
  4079. if (hkRoot) RegCloseKey(hkRoot);
  4080. //
  4081. // Regardless of what happened, return success
  4082. //
  4083. dwError = ERROR_SUCCESS;
  4084. }
  4085. return dwError;
  4086. }
  4087. /*++
  4088. Routine Name
  4089. ClusterSplReadUpgradeKey
  4090. Routine Description:
  4091. After the first reboot following an upgrade of a node, the cluster
  4092. service informs the resdll that a version change occured. At this
  4093. time out spooler resource may be running on another node or may
  4094. not be actie at all. Thus the resdll writes a value in the local
  4095. registry. The vaue name is the GUID for the spooler resource, the
  4096. value is DWORD 1. When the cluster spooler resource fails over on this
  4097. machine it (i.e. now) it queries for that value to know if it needs
  4098. to preform post upgrade operations, like upgrading the printer drivers.
  4099. After the spooler preforms upgrade taks, it will delete the value
  4100. corresponding to its GUID. Also if that value is the only one under the
  4101. SPLREG_CLUSTER_UPGRADE_KEY key, it will delete that key.
  4102. Arguments:
  4103. pszResource - string respresenation of GUID for the cluster resource
  4104. Return Value:
  4105. WIN32 error code
  4106. --*/
  4107. DWORD
  4108. ClusterSplDeleteUpgradeKey(
  4109. IN LPCWSTR pszResourceID
  4110. )
  4111. {
  4112. DWORD dwError = ERROR_INVALID_PARAMETER;
  4113. HKEY hkRoot = NULL;
  4114. HKEY hkUpgrade = NULL;
  4115. if ((dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  4116. SPLREG_CLUSTER_LOCAL_ROOT_KEY,
  4117. 0,
  4118. KEY_READ,
  4119. &hkRoot)) == ERROR_SUCCESS &&
  4120. (dwError = RegOpenKeyEx(hkRoot,
  4121. SPLREG_CLUSTER_UPGRADE_KEY,
  4122. 0,
  4123. KEY_ALL_ACCESS,
  4124. &hkUpgrade)) == ERROR_SUCCESS)
  4125. {
  4126. DWORD cValues = 0;
  4127. dwError = RegDeleteValue(hkUpgrade, pszResourceID);
  4128. if (RegQueryInfoKey(hkUpgrade,
  4129. NULL,
  4130. NULL,
  4131. NULL,
  4132. NULL,
  4133. NULL,
  4134. NULL,
  4135. &cValues,
  4136. NULL,
  4137. NULL,
  4138. NULL,
  4139. NULL) == ERROR_SUCCESS && !cValues)
  4140. {
  4141. RegDeleteKey(hkRoot, SPLREG_CLUSTER_UPGRADE_KEY);
  4142. }
  4143. if (hkUpgrade) RegCloseKey(hkUpgrade);
  4144. if (hkRoot) RegCloseKey(hkRoot);
  4145. }
  4146. return dwError;
  4147. }
  4148. /*++
  4149. Routine Name
  4150. RunProcess
  4151. Routine Description:
  4152. Creates a process. Waits for it to terminate.
  4153. Arguments:
  4154. pszExe - program to execute (muist be fully qualfied)
  4155. pszCommand - command line to execute
  4156. dwTimeOut - time to wait for the process to terminate
  4157. pszExitCode - pointer to reveice exit code of process
  4158. Return Value:
  4159. WIN32 error code
  4160. --*/
  4161. DWORD
  4162. RunProcess(
  4163. IN LPCWSTR pszExe,
  4164. IN LPCWSTR pszCommand,
  4165. IN DWORD dwTimeOut,
  4166. OUT LPDWORD pdwExitCode OPTIONAL
  4167. )
  4168. {
  4169. DWORD dwError = ERROR_INVALID_PARAMETER;
  4170. if (pszCommand && pszExe)
  4171. {
  4172. PROCESS_INFORMATION ProcInfo = {0};
  4173. STARTUPINFO StartInfo = {0};
  4174. StartInfo.cb = sizeof(StartInfo);
  4175. StartInfo.dwFlags = 0;
  4176. if (!CreateProcess(pszExe,
  4177. (LPWSTR)pszCommand,
  4178. NULL,
  4179. NULL,
  4180. FALSE,
  4181. CREATE_NO_WINDOW,
  4182. NULL,
  4183. NULL,
  4184. &StartInfo,
  4185. &ProcInfo))
  4186. {
  4187. dwError = GetLastError();
  4188. }
  4189. else
  4190. {
  4191. if (WaitForSingleObject(ProcInfo.hProcess, dwTimeOut) == WAIT_OBJECT_0)
  4192. {
  4193. //
  4194. // Process executed fine
  4195. //
  4196. dwError = ERROR_SUCCESS;
  4197. }
  4198. if (pdwExitCode && !GetExitCodeProcess(ProcInfo.hProcess, pdwExitCode))
  4199. {
  4200. *pdwExitCode = 0;
  4201. }
  4202. CloseHandle(ProcInfo.hThread);
  4203. CloseHandle(ProcInfo.hProcess);
  4204. }
  4205. }
  4206. return dwError;
  4207. }
  4208. /*++
  4209. Routine Name
  4210. GetLocalArchEnv
  4211. Routine Description:
  4212. Helper function. Returns a pointer to the environment
  4213. that matches the architecture of the local machine.
  4214. The environemnt is taken off the pInSpooler passed as
  4215. argument
  4216. Arguments:
  4217. pIniSpooler - pointer to spooler structure
  4218. Return Value:
  4219. PINIENVIRONMENT
  4220. --*/
  4221. PINIENVIRONMENT
  4222. GetLocalArchEnv(
  4223. IN PINISPOOLER pIniSpooler
  4224. )
  4225. {
  4226. SplInSem();
  4227. //
  4228. // The local spooler and cluster spooler do not share the same Environment structures.
  4229. //
  4230. return pIniSpooler && pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ?
  4231. FindEnvironment(szEnvironment, pIniSpooler) : pThisEnvironment;
  4232. }
  4233. /*++
  4234. Routine Name
  4235. ClusterFindLanguageMonitor
  4236. Routine Description:
  4237. If a valid monitor name is specified and the monitor
  4238. is not found in the specified pIniSpooler, then the
  4239. function will try to install the monitor from the
  4240. cluster disk.
  4241. Arguments:
  4242. pszMonitor - monitor name
  4243. pszEnvName - name of environment of the lm
  4244. pIniSpooler - pointer to cluster spooler structure
  4245. Return Value:
  4246. Win32 error code
  4247. --*/
  4248. DWORD
  4249. ClusterFindLanguageMonitor(
  4250. IN LPCWSTR pszMonitor,
  4251. IN LPCWSTR pszEnvName,
  4252. IN PINISPOOLER pIniSpooler
  4253. )
  4254. {
  4255. DWORD dwError = ERROR_SUCCESS;
  4256. //
  4257. // This is the moment where we check if we need to add the monitor
  4258. //
  4259. if (pszMonitor && *pszMonitor)
  4260. {
  4261. PINIMONITOR pIniLangMonitor;
  4262. EnterSplSem();
  4263. //
  4264. // We need to find the language monitor in pLocalIniSpooler
  4265. // LMs are not cluster aware, so the cluster pIniSpooler doesn't know about them
  4266. //
  4267. pIniLangMonitor = FindMonitor(pszMonitor, pLocalIniSpooler);
  4268. LeaveSplSem();
  4269. if (!pIniLangMonitor)
  4270. {
  4271. PINIENVIRONMENT pIniEnvironment;
  4272. EnterSplSem();
  4273. pIniEnvironment = FindEnvironment(pszEnvName, pIniSpooler);
  4274. LeaveSplSem();
  4275. if (pIniEnvironment)
  4276. {
  4277. DBGMSG(DBG_CLUSTER, ("ClusterFindLanguageMonitor Trying to install LangMonitor "TSTR"\n", pszMonitor));
  4278. //
  4279. // We try to install the monitor from the cluster disk to the local spooler
  4280. //
  4281. dwError = InstallMonitorFromCluster(pszMonitor,
  4282. pIniEnvironment->pName,
  4283. pIniEnvironment->pDirectory,
  4284. pIniSpooler);
  4285. }
  4286. else
  4287. {
  4288. dwError = ERROR_INVALID_ENVIRONMENT;
  4289. }
  4290. }
  4291. }
  4292. DBGMSG(DBG_CLUSTER, ("ClusterFindLanguageMonitor LangMonitor "TSTR" return Win32 error %u\n", pszMonitor, dwError));
  4293. return dwError;
  4294. }
  4295. /*++
  4296. Routine Name
  4297. WriteTimeStamp
  4298. Routine Description:
  4299. Opens the key hkRoot\pszSubkey1\...\pszSubKey5
  4300. and writes the value of szClusDrvTimeStamp (binary
  4301. data representing a system time)
  4302. Arguments:
  4303. hkRoot - handle to driver key
  4304. SysTime - system time structure
  4305. pszSubKey1 - subkey of the root key
  4306. pszSubKey2 - subkey of key1, can be null
  4307. pszSubKey3 - subkey of key2, can be null
  4308. pszSubKey4 - subkey of key3, can be null
  4309. pszSubKey5 - subkey of key4, can be null
  4310. pIniSpooler - spooler, can be NULL
  4311. Return Value:
  4312. WIN32 error code
  4313. --*/
  4314. DWORD
  4315. WriteTimeStamp(
  4316. IN HKEY hkRoot,
  4317. IN SYSTEMTIME SysTime,
  4318. IN LPCWSTR pszSubKey1,
  4319. IN LPCWSTR pszSubKey2,
  4320. IN LPCWSTR pszSubKey3,
  4321. IN LPCWSTR pszSubKey4,
  4322. IN LPCWSTR pszSubKey5,
  4323. IN PINISPOOLER pIniSpooler
  4324. )
  4325. {
  4326. DWORD dwError = ERROR_INVALID_PARAMETER;
  4327. if (hkRoot)
  4328. {
  4329. LPCWSTR ppszKeyNames[] = {NULL, pszSubKey1, pszSubKey2, pszSubKey3, pszSubKey4, pszSubKey5};
  4330. HKEY pKeyHandles[] = {hkRoot, NULL, NULL, NULL, NULL, NULL};
  4331. DWORD uIndex;
  4332. dwError = ERROR_SUCCESS;
  4333. //
  4334. // Open all the keys
  4335. //
  4336. for (uIndex = 1;
  4337. uIndex < COUNTOF(ppszKeyNames) &&
  4338. dwError == ERROR_SUCCESS &&
  4339. ppszKeyNames[uIndex];
  4340. uIndex++)
  4341. {
  4342. DBGMSG(DBG_CLUSTER, ("KEY "TSTR"\n", ppszKeyNames[uIndex]));
  4343. dwError = SplRegCreateKey(pKeyHandles[uIndex-1],
  4344. ppszKeyNames[uIndex],
  4345. 0,
  4346. KEY_ALL_ACCESS,
  4347. NULL,
  4348. &pKeyHandles[uIndex],
  4349. NULL,
  4350. pIniSpooler);
  4351. }
  4352. //
  4353. // If we opened successfully the keys that we wanted, write the value
  4354. //
  4355. if (dwError == ERROR_SUCCESS &&
  4356. !RegSetBinaryData(pKeyHandles[uIndex-1],
  4357. szClusDrvTimeStamp,
  4358. (LPBYTE)&SysTime,
  4359. sizeof(SysTime),
  4360. &dwError,
  4361. pIniSpooler))
  4362. {
  4363. dwError = GetLastError();
  4364. }
  4365. //
  4366. // Close any keys that we opened
  4367. //
  4368. for (uIndex = 1; uIndex < COUNTOF(ppszKeyNames); uIndex++)
  4369. {
  4370. if (pKeyHandles[uIndex])
  4371. {
  4372. SplRegCloseKey(pKeyHandles[uIndex], pIniSpooler);
  4373. }
  4374. }
  4375. }
  4376. DBGMSG(DBG_CLUSTER, ("WriteTimeStamp returns Win32 error %u\n", dwError));
  4377. return dwError;
  4378. }
  4379. /*++
  4380. Routine Name
  4381. ReadTimeStamp
  4382. Routine Description:
  4383. Opens the key hkRoot\pszSubkey1\...\pszSubKey5
  4384. and reads the value of szClusDrvTimeStamp (binary
  4385. data representing a system time)
  4386. Arguments:
  4387. hkRoot - handle to driver key
  4388. pSysTime - pointer to allocated system time structure
  4389. pszSubKey1 - subkey of the root key
  4390. pszSubKey2 - subkey of key1, can be null
  4391. pszSubKey3 - subkey of key2, can be null
  4392. pszSubKey4 - subkey of key3, can be null
  4393. pszSubKey5 - subkey of key4, can be null
  4394. pIniSpooler - spooler, can be NULL
  4395. Return Value:
  4396. WIN32 error code
  4397. --*/
  4398. DWORD
  4399. ReadTimeStamp(
  4400. IN HKEY hkRoot,
  4401. IN OUT SYSTEMTIME *pSysTime,
  4402. IN LPCWSTR pszSubKey1,
  4403. IN LPCWSTR pszSubKey2,
  4404. IN LPCWSTR pszSubKey3,
  4405. IN LPCWSTR pszSubKey4,
  4406. IN LPCWSTR pszSubKey5,
  4407. IN PINISPOOLER pIniSpooler
  4408. )
  4409. {
  4410. DWORD dwError = ERROR_INVALID_PARAMETER;
  4411. if (hkRoot && pSysTime)
  4412. {
  4413. LPCWSTR ppszKeyNames[] = {NULL, pszSubKey1, pszSubKey2, pszSubKey3, pszSubKey4, pszSubKey5};
  4414. HKEY pKeyHandles[] = {hkRoot, NULL, NULL, NULL, NULL, NULL};
  4415. DWORD uIndex;
  4416. dwError = ERROR_SUCCESS;
  4417. //
  4418. // Open all the keys
  4419. //
  4420. for (uIndex = 1;
  4421. uIndex < COUNTOF(ppszKeyNames) &&
  4422. dwError == ERROR_SUCCESS &&
  4423. ppszKeyNames[uIndex];
  4424. uIndex++)
  4425. {
  4426. dwError = SplRegCreateKey(pKeyHandles[uIndex-1],
  4427. ppszKeyNames[uIndex],
  4428. 0,
  4429. KEY_ALL_ACCESS,
  4430. NULL,
  4431. &pKeyHandles[uIndex],
  4432. NULL,
  4433. pIniSpooler);
  4434. }
  4435. //
  4436. // If we opened successfully the keys that we wanted, write the value
  4437. //
  4438. if (dwError == ERROR_SUCCESS)
  4439. {
  4440. DWORD cbSize = sizeof(SYSTEMTIME);
  4441. dwError = SplRegQueryValue(pKeyHandles[uIndex-1],
  4442. szClusDrvTimeStamp,
  4443. NULL,
  4444. (LPBYTE)pSysTime,
  4445. &cbSize,
  4446. pIniSpooler);
  4447. }
  4448. //
  4449. // Close any keys that we opened
  4450. //
  4451. for (uIndex = 1; uIndex < COUNTOF(ppszKeyNames); uIndex++)
  4452. {
  4453. if (pKeyHandles[uIndex])
  4454. {
  4455. SplRegCloseKey(pKeyHandles[uIndex], pIniSpooler);
  4456. }
  4457. }
  4458. }
  4459. DBGMSG(DBG_CLUSTER, ("ReadTimeStamp returns Win32 error %u\n", dwError));
  4460. return dwError;
  4461. }
  4462. /*++
  4463. Routine Name
  4464. ClusterCheckDriverChanged
  4465. Routine Description:
  4466. Helper function for spooler start up. When we have a cluster spooler
  4467. and we build the environments and drivers, we need to check if the
  4468. drivers on the local machine (in print$\GUID) are in sync with the
  4469. drivers on the cluster disk. We store a time stamp in the cluster
  4470. data base. The time stamp indicate when the last update on the driver
  4471. occured. The same type of time stamp is stroed in the lcoal registry
  4472. (for our cluster spooler). If the 2 time stamp are different, then
  4473. we need to call an add printer driver and use data from the cluster
  4474. disk. The drivers on the cluster spooler were updated while it was
  4475. running on a different node.
  4476. Arguments:
  4477. hClusterVersionKey - handle to the driver version key
  4478. pszDriver - driver name
  4479. pszEnv - driver environment name
  4480. pszVer - driver version name
  4481. pIniSpooler - spooler
  4482. Return Value:
  4483. TRUE - if the driver on the cluster disk is updated and we need to
  4484. call add printer driver. If anything fails in this function,
  4485. then we also return TRUE, to force our caller to update/add
  4486. the driver in question.
  4487. FALSE - if the drivers on the local mahcine and the cluster spooler
  4488. are in sync
  4489. --*/
  4490. BOOL
  4491. ClusterCheckDriverChanged(
  4492. IN HKEY hClusterVersionKey,
  4493. IN LPCWSTR pszDriver,
  4494. IN LPCWSTR pszEnv,
  4495. IN LPCWSTR pszVer,
  4496. IN PINISPOOLER pIniSpooler
  4497. )
  4498. {
  4499. BOOL bReturn = TRUE;
  4500. if (hClusterVersionKey &&
  4501. pszDriver &&
  4502. pszEnv &&
  4503. pszVer)
  4504. {
  4505. SYSTEMTIME ClusterTime;
  4506. SYSTEMTIME NodeTime;
  4507. if (ReadTimeStamp(hClusterVersionKey,
  4508. &ClusterTime,
  4509. pszDriver,
  4510. NULL,
  4511. NULL,
  4512. NULL,
  4513. NULL,
  4514. pIniSpooler) == ERROR_SUCCESS &&
  4515. ReadTimeStamp(HKEY_LOCAL_MACHINE,
  4516. &NodeTime,
  4517. ipszRegistryClusRepository,
  4518. pIniSpooler->pszClusResID,
  4519. pszEnv,
  4520. pszVer,
  4521. pszDriver,
  4522. NULL) == ERROR_SUCCESS &&
  4523. !memcmp(&ClusterTime, &NodeTime, sizeof(SYSTEMTIME)))
  4524. {
  4525. bReturn = FALSE;
  4526. }
  4527. }
  4528. DBGMSG(DBG_CLUSTER, ("ClusterCheckDriverChanged returns bool %u\n", bReturn));
  4529. return bReturn;
  4530. }
  4531. /*++
  4532. Routine Name
  4533. IsLocalFile
  4534. Routine Description:
  4535. Checks if a file is on the local machine. If the file path is
  4536. "\\machinename\sharename\...\filename" then machinename is checked
  4537. against pIniSpooler->pMachineName and pIniSpooler->ppszOtherNames.
  4538. Arguments:
  4539. pszFileName - file name
  4540. pIniSpooler - INISPOOLER structure
  4541. Return Value:
  4542. TRUE if the file is placed locally.
  4543. FALSE if the file is placed remotely.
  4544. --*/
  4545. BOOL
  4546. IsLocalFile (
  4547. IN LPCWSTR pszFileName,
  4548. IN PINISPOOLER pIniSpooler
  4549. )
  4550. {
  4551. LPWSTR pEndOfMachineName, pMachineName;
  4552. BOOL bRetValue = TRUE;
  4553. if (pszFileName &&
  4554. *pszFileName == L'\\' &&
  4555. *(pszFileName+1) == L'\\')
  4556. {
  4557. //
  4558. // If first 2 charactes in pszFileName are '\\',
  4559. // then search for the next '\\'. If found, then set it to 0,
  4560. // to isolate the machine name.
  4561. //
  4562. pMachineName = (LPWSTR)pszFileName;
  4563. if (pEndOfMachineName = wcschr(pszFileName + 2, L'\\'))
  4564. {
  4565. *pEndOfMachineName = 0;
  4566. }
  4567. bRetValue = CheckMyName(pMachineName, pIniSpooler);
  4568. //
  4569. // Restore pszFileName.
  4570. //
  4571. if (pEndOfMachineName)
  4572. {
  4573. *pEndOfMachineName = L'\\';
  4574. }
  4575. }
  4576. return bRetValue;
  4577. }
  4578. /*++
  4579. Routine Name
  4580. IsEXEFile
  4581. Routine Description:
  4582. Checks if a file is a executable.
  4583. The check is made against file extension, which isn't quite
  4584. accurate.
  4585. Arguments:
  4586. pszFileName - file name
  4587. Return Value:
  4588. TRUE if the file extension is either .EXE or .DLL
  4589. --*/
  4590. BOOL
  4591. IsEXEFile(
  4592. IN LPCWSTR pszFileName
  4593. )
  4594. {
  4595. BOOL bRetValue = FALSE;
  4596. DWORD dwLength;
  4597. LPWSTR pszExtension;
  4598. if (pszFileName && *pszFileName)
  4599. {
  4600. dwLength = wcslen(pszFileName);
  4601. if (dwLength > COUNTOF(L".EXE") - 1)
  4602. {
  4603. pszExtension = (LPWSTR)pszFileName + dwLength - (COUNTOF(L".EXE") - 1);
  4604. if (_wcsicmp(pszExtension , L".EXE") == 0 ||
  4605. _wcsicmp(pszExtension , L".DLL") == 0)
  4606. {
  4607. bRetValue = TRUE;
  4608. }
  4609. }
  4610. }
  4611. return bRetValue;
  4612. }
  4613. /*++
  4614. Routine Name
  4615. PackStringToEOB
  4616. Routine Description:
  4617. Copies a string to the end of buffer.
  4618. The buffer must be big enough so that it can hold the string.
  4619. This function is called by Get/Enum APIs that build BLOB buffers to
  4620. send them with RPC.
  4621. Arguments:
  4622. pszSource - string to by copied to the end of buffer
  4623. pEnd - a pointer to the end of a pre-allocated buffer.
  4624. Return Value:
  4625. The pointer to the end of buffer after the sting was appended.
  4626. NULL if an error occured.
  4627. --*/
  4628. LPBYTE
  4629. PackStringToEOB(
  4630. IN LPWSTR pszSource,
  4631. IN LPBYTE pEnd
  4632. )
  4633. {
  4634. DWORD cbStr;
  4635. //
  4636. // Align the end of buffer to WORD boundaries.
  4637. //
  4638. WORD_ALIGN_DOWN(pEnd);
  4639. if (pszSource && pEnd)
  4640. {
  4641. cbStr = (wcslen(pszSource) + 1) * sizeof(WCHAR);
  4642. pEnd -= cbStr;
  4643. CopyMemory(pEnd, pszSource, cbStr);
  4644. }
  4645. else
  4646. {
  4647. pEnd = NULL;
  4648. }
  4649. return pEnd;
  4650. }
  4651. LPVOID
  4652. MakePTR (
  4653. IN LPVOID pBuf,
  4654. IN DWORD Quantity
  4655. )
  4656. /*++
  4657. Routine Name
  4658. MakePTR
  4659. Routine Description:
  4660. Makes a pointer by adding a quantity to the beginning of a buffer.
  4661. Arguments:
  4662. pBuf - pointer to buffer
  4663. DWORD - quantity
  4664. Return Value:
  4665. LPVOID pointer
  4666. --*/
  4667. {
  4668. return (LPVOID)((ULONG_PTR)pBuf + (ULONG_PTR)Quantity);
  4669. }
  4670. DWORD
  4671. MakeOffset (
  4672. IN LPVOID pFirst,
  4673. IN LPVOID pSecond
  4674. )
  4675. /*++
  4676. Routine Name
  4677. MakeOffset
  4678. Routine Description:
  4679. Substarcts two pointers.
  4680. Arguments:
  4681. pFirst - pointer to buffer
  4682. pSecond - pointer to buffer
  4683. Return Value:
  4684. DWORD
  4685. --*/
  4686. {
  4687. return (DWORD)((ULONG_PTR)pFirst - (ULONG_PTR)pSecond);
  4688. }
  4689. /*++
  4690. Routine Name
  4691. IsValidPrinterName
  4692. Routine Description:
  4693. Checks if a string is a valid printer name.
  4694. Arguments:
  4695. pszPrinter - pointer to string
  4696. cchMax - max number of chars to scan
  4697. Return Value:
  4698. TRUE - the string is a valid printer name
  4699. FALSE - the string is a invalid printer name. The function set the last error
  4700. to ERROR_INVALID_PRINTER_NAME in this case
  4701. --*/
  4702. BOOL
  4703. IsValidPrinterName(
  4704. IN LPCWSTR pszPrinter,
  4705. IN DWORD cchMax
  4706. )
  4707. {
  4708. DWORD Error = ERROR_INVALID_PRINTER_NAME;
  4709. //
  4710. // A printer name is of the form:
  4711. //
  4712. // \\s\p or p
  4713. //
  4714. // The name cannot contain the , character. Note that the add printer
  4715. // wizard doesn't accept "!" as a valid printer name. We wanted to do
  4716. // the same here, but we regressed in app compat with 9x apps.
  4717. // The number of \ in the name is 0 or 3
  4718. // If the name contains \, then the fist 2 chars must be \.
  4719. // The printer name cannot end in \.
  4720. // After leading "\\" then next char must not be \
  4721. // The minimum length is 1 character
  4722. // The maximum length is MAX_UNC_PRINTER_NAME
  4723. //
  4724. if (!IsBadStringPtr(pszPrinter, cchMax) && pszPrinter && *pszPrinter)
  4725. {
  4726. UINT uSlashCount = 0;
  4727. UINT uLen = 0;
  4728. LPCWSTR p;
  4729. Error = ERROR_SUCCESS;
  4730. //
  4731. // Count characters
  4732. //
  4733. for (p = pszPrinter; *p && uLen <= cchMax; p++, uLen++)
  4734. {
  4735. if (*p == L',')
  4736. {
  4737. Error = ERROR_INVALID_PRINTER_NAME;
  4738. break;
  4739. }
  4740. else if (*p == L'\\')
  4741. {
  4742. uSlashCount++;
  4743. }
  4744. }
  4745. //
  4746. // Perform validation
  4747. //
  4748. if (Error == ERROR_SUCCESS &&
  4749. //
  4750. // Validate length
  4751. //
  4752. (uLen > cchMax ||
  4753. //
  4754. // The printer name has either no \, or exactly 3 \.
  4755. //
  4756. uSlashCount && uSlashCount != 3 ||
  4757. //
  4758. // A printer name that contains 3 \, must have the first 2 chars \ and the 3 not \.
  4759. // The last char cannot be \.
  4760. // Ex "\Foo", "F\oo", "\\\Foo", "\\Foo\" are invalid.
  4761. // Ex. "\\srv\bar" is valid.
  4762. //
  4763. uSlashCount == 3 && (pszPrinter[0] != L'\\' ||
  4764. pszPrinter[1] != L'\\' ||
  4765. pszPrinter[2] == L'\\' ||
  4766. pszPrinter[uLen-1] == L'\\')))
  4767. {
  4768. Error = ERROR_INVALID_PRINTER_NAME;
  4769. }
  4770. }
  4771. SetLastError(Error);
  4772. return Error == ERROR_SUCCESS;
  4773. }
  4774. /*++
  4775. Routine Name
  4776. SplPowerEvent
  4777. Routine Description:
  4778. Checks if the spooler is ready for power management events like hibernation/stand by.
  4779. If we have printing jobs that are not in an error state or offline, then we deny the
  4780. powering down request.
  4781. Arguments:
  4782. Event - power management event
  4783. Return Value:
  4784. TRUE - the spooler allowd the system to be powered down
  4785. FALSE - the spooler denies the request for powering down
  4786. --*/
  4787. BOOL
  4788. SplPowerEvent(
  4789. DWORD Event
  4790. )
  4791. {
  4792. BOOL bAllow = TRUE;
  4793. EnterSplSem();
  4794. switch (Event)
  4795. {
  4796. case PBT_APMQUERYSUSPEND:
  4797. {
  4798. PINISPOOLER pIniSpooler;
  4799. for (pIniSpooler = pLocalIniSpooler;
  4800. pIniSpooler && bAllow;
  4801. pIniSpooler = pIniSpooler->pIniNextSpooler)
  4802. {
  4803. PINIPRINTER pIniPrinter;
  4804. for (pIniPrinter = pIniSpooler->pIniPrinter;
  4805. pIniPrinter && bAllow;
  4806. pIniPrinter = pIniPrinter->pNext)
  4807. {
  4808. PINIJOB pIniJob;
  4809. for (pIniJob = pIniPrinter->pIniFirstJob;
  4810. pIniJob && bAllow;
  4811. pIniJob = pIniJob->pIniNextJob)
  4812. {
  4813. if (pIniJob->Status & JOB_PRINTING &&
  4814. !(pIniJob->Status & JOB_ERROR | pIniJob->Status & JOB_OFFLINE))
  4815. {
  4816. bAllow = FALSE;
  4817. }
  4818. }
  4819. }
  4820. }
  4821. //
  4822. // If we allow system power down, then we need to stop scheduling jobs
  4823. //
  4824. if (bAllow)
  4825. {
  4826. ResetEvent(PowerManagementSignal);
  4827. }
  4828. break;
  4829. }
  4830. case PBT_APMQUERYSUSPENDFAILED:
  4831. case PBT_APMRESUMESUSPEND:
  4832. case PBT_APMRESUMEAUTOMATIC:
  4833. //
  4834. // Set the event to allow the spooler to continue scheudling jobs
  4835. //
  4836. SetEvent(PowerManagementSignal);
  4837. break;
  4838. default:
  4839. //
  4840. // We ignore any other power management event
  4841. //
  4842. break;
  4843. }
  4844. LeaveSplSem();
  4845. return bAllow;
  4846. }
  4847. /*++
  4848. Routine Name
  4849. IsCallViaRPC
  4850. Routine Description:
  4851. Checks if the caller of this function came in the spooler server via RPC or not.
  4852. Arguments:
  4853. None
  4854. Return Value:
  4855. TRUE - the caller came in via RPC
  4856. FALSE - the caller did not come via RPC
  4857. --*/
  4858. BOOL
  4859. IsCallViaRPC(
  4860. IN VOID
  4861. )
  4862. {
  4863. UINT uType;
  4864. return I_RpcBindingInqTransportType(NULL, &uType) == RPC_S_NO_CALL_ACTIVE ? FALSE : TRUE;
  4865. }
  4866. /*++
  4867. Routine Name
  4868. MergeMultiSz
  4869. Routine Description:
  4870. This merges two multisz strings such that there is a resulting multisz
  4871. strings that has no duplicate strings internal. This algorithm is
  4872. currently N^2 which could be improved. It is currently being called from
  4873. the driver code and the dependent files are not a large set.
  4874. Arguments:
  4875. pszMultiSz1 - The first multi-sz string.
  4876. cchMultiSz1 - The length of the multi-sz string.
  4877. pszMultiSz2 - The second multi-sz string.
  4878. cchMultiSz2 - The length of the second multi-sz string.
  4879. ppszMultiSzMerge - The merged multi-sz string.
  4880. pcchMultiSzMerge - The number of characters in the merge, this could be
  4881. less than the allocated buffer size.
  4882. Return Value:
  4883. FALSE on failure, LastError is set.
  4884. --*/
  4885. BOOL
  4886. MergeMultiSz(
  4887. IN PCWSTR pszMultiSz1,
  4888. IN DWORD cchMultiSz1,
  4889. IN PCWSTR pszMultiSz2,
  4890. IN DWORD cchMultiSz2,
  4891. OUT PWSTR *ppszMultiSzMerge,
  4892. OUT DWORD *pcchMultiSzMerge OPTIONAL
  4893. )
  4894. {
  4895. BOOL bRet = FALSE;
  4896. PWSTR pszNewMultiSz = NULL;
  4897. DWORD cchNewMultiSz = 0;
  4898. *ppszMultiSzMerge = NULL;
  4899. if (pcchMultiSzMerge)
  4900. {
  4901. *pcchMultiSzMerge = 0;
  4902. }
  4903. if (cchMultiSz1 || cchMultiSz2)
  4904. {
  4905. //
  4906. // Code assumes that these are at least 1 in the allocation size.
  4907. //
  4908. cchMultiSz1 = cchMultiSz1 == 0 ? 1 : cchMultiSz1;
  4909. cchMultiSz2 = cchMultiSz2 == 0 ? 1 : cchMultiSz2;
  4910. //
  4911. // The merged strings will be at most the size of both of them (if there are
  4912. // no duplicates).
  4913. //
  4914. pszNewMultiSz = AllocSplMem((cchMultiSz1 + cchMultiSz2 - 1) * sizeof(WCHAR));
  4915. bRet = pszNewMultiSz != NULL;
  4916. if (bRet)
  4917. {
  4918. //
  4919. // Ensure that the multi-sz string is at least empty.
  4920. //
  4921. *pszNewMultiSz = L'\0';
  4922. }
  4923. if (bRet && pszMultiSz1)
  4924. {
  4925. AddMultiSzNoDuplicates(pszMultiSz1, pszNewMultiSz);
  4926. }
  4927. if (bRet && pszMultiSz2)
  4928. {
  4929. AddMultiSzNoDuplicates(pszMultiSz2, pszNewMultiSz);
  4930. }
  4931. if (bRet)
  4932. {
  4933. cchNewMultiSz = GetMultiSZLen(pszNewMultiSz);
  4934. }
  4935. }
  4936. if (bRet)
  4937. {
  4938. *ppszMultiSzMerge = pszNewMultiSz;
  4939. if (pcchMultiSzMerge)
  4940. {
  4941. *pcchMultiSzMerge = cchNewMultiSz;
  4942. }
  4943. pszNewMultiSz = NULL;
  4944. }
  4945. FreeSplMem(pszNewMultiSz);
  4946. return bRet;
  4947. }
  4948. /*++
  4949. Routine Name
  4950. AddMultiSzNoDuplicates
  4951. Routine Description:
  4952. This adds all of the strings in a multisz string to a buffer (the buffer
  4953. must be guaranteed to be large enough to accept the strings), it makes
  4954. sure that there are no case insensitive duplicates in the list.
  4955. Arguments:
  4956. pszMultiSzIn - The multi-sz whose elements are being added.
  4957. pszNewMultiSz - The buffer in which we are filling up the multi-sz
  4958. Return Value:
  4959. None.
  4960. --*/
  4961. VOID
  4962. AddMultiSzNoDuplicates(
  4963. IN PCWSTR pszMultiSzIn,
  4964. IN OUT PWSTR pszNewMultiSz
  4965. )
  4966. {
  4967. PCWSTR pszIn = NULL;
  4968. for(pszIn = pszMultiSzIn; *pszIn; pszIn += wcslen(pszIn) + 1)
  4969. {
  4970. BOOL bStringFound = FALSE;
  4971. PWSTR pszMerge = NULL;
  4972. //
  4973. // For each input string, run the merged multi-sz string and add it if
  4974. // it is not already there.
  4975. //
  4976. for(pszMerge = pszNewMultiSz; *pszMerge; pszMerge += wcslen(pszMerge) + 1)
  4977. {
  4978. if (!_wcsicmp(pszIn, pszMerge))
  4979. {
  4980. bStringFound = TRUE;
  4981. break;
  4982. }
  4983. }
  4984. //
  4985. // If the string was not found in the multisz string, then add it to the end.
  4986. //
  4987. if (!bStringFound)
  4988. {
  4989. wcscpy(pszMerge, pszIn);
  4990. pszMerge += wcslen(pszIn) + 1;
  4991. //
  4992. // Add the extra null terminator for now.
  4993. //
  4994. *pszMerge = '\0';
  4995. }
  4996. }
  4997. }
  4998. /*++
  4999. Routine Name
  5000. GetMultiSZLen
  5001. Routine Description:
  5002. This returns the number of characters in a multisz string, including NULLs.
  5003. Arguments:
  5004. pMultiSzSrc - The multisz string to search.
  5005. Return Value:
  5006. The number of characters in the string.
  5007. --*/
  5008. DWORD
  5009. GetMultiSZLen(
  5010. IN LPWSTR pMultiSzSrc
  5011. )
  5012. {
  5013. DWORD dwLen = 0;
  5014. LPWSTR pTmp = pMultiSzSrc;
  5015. while( TRUE ) {
  5016. dwLen += wcslen(pTmp) + 1; // Incude the terminating NULL char
  5017. pTmp = pMultiSzSrc + dwLen; // Point to the beginning of the next string in the MULTI_SZ
  5018. if( !*pTmp )
  5019. return ++dwLen; // Reached the end of the MULTI_SZ string. Add 1 to the count for the last NULL char.
  5020. }
  5021. }