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

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