Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2134 lines
60 KiB

  1. /*++
  2. Copyright (c) 1990 - 1996 Microsoft Corporation
  3. Module Name:
  4. openprn.c
  5. Abstract:
  6. This module provides all the public exported APIs relating to Printer
  7. management for the Local Print Providor
  8. LocalOpenPrinter
  9. SplClosePrinter
  10. Author:
  11. Dave Snipp (DaveSn) 15-Mar-1991
  12. Revision History:
  13. Matthew A Felton (mattfe) June 1994 RapidPrint
  14. Jan 95 Cleanup CreatePrinterHandle
  15. --*/
  16. #define NOMINMAX
  17. #include <precomp.h>
  18. #include "jobid.h"
  19. #include "filepool.hxx"
  20. #define SZXCVPORT L"XcvPort "
  21. #define SZXCVMONITOR L"XcvMonitor "
  22. LPCTSTR pszLocalOnlyToken = L"LocalOnly";
  23. LPCTSTR pszLocalsplOnlyToken = L"LocalsplOnly";
  24. HANDLE
  25. CreatePrinterHandle(
  26. LPWSTR pPrinterName,
  27. LPWSTR pFullMachineName,
  28. PINIPRINTER pIniPrinter,
  29. PINIPORT pIniPort,
  30. PINIPORT pIniNetPort,
  31. PINIJOB pIniJob,
  32. DWORD TypeofHandle,
  33. HANDLE hPort,
  34. PPRINTER_DEFAULTS pDefaults,
  35. PINISPOOLER pIniSpooler,
  36. DWORD DesiredAccess,
  37. LPBYTE pSplClientInfo,
  38. DWORD dwLevel,
  39. HANDLE hReadFile
  40. )
  41. {
  42. PSPOOL pSpool = NULL;
  43. BOOL bStatus = FALSE;
  44. HANDLE hReturnHandle = NULL;
  45. LPDEVMODE pDevMode = NULL;
  46. PSPLCLIENT_INFO_1 pSplClientInfo1 = (PSPLCLIENT_INFO_1)pSplClientInfo;
  47. DWORD ObjectType;
  48. SPLASSERT( pIniSpooler->signature == ISP_SIGNATURE );
  49. if ( dwLevel && ( dwLevel != 1 || !pSplClientInfo) ) {
  50. DBGMSG(DBG_ERROR,
  51. ("CreatePrintHandle: Invalid client info %x - %d\n",
  52. pSplClientInfo, dwLevel));
  53. pSplClientInfo = NULL;
  54. }
  55. try {
  56. pSpool = (PSPOOL)AllocSplMem( SPOOL_SIZE );
  57. if ( pSpool == NULL ) {
  58. DBGMSG( DBG_WARNING, ("CreatePrinterHandle failed to allocate SPOOL %d\n", GetLastError() ));
  59. leave;
  60. }
  61. pSpool->signature = SJ_SIGNATURE;
  62. pSpool->pIniPrinter = pIniPrinter;
  63. pSpool->hReadFile = hReadFile;
  64. pSpool->pIniPort = pIniPort;
  65. pSpool->pIniNetPort = pIniNetPort;
  66. pSpool->pIniJob = pIniJob;
  67. pSpool->TypeofHandle = TypeofHandle;
  68. pSpool->hPort = hPort;
  69. pSpool->Status = 0;
  70. pSpool->pDevMode = NULL;
  71. pSpool->pName = AllocSplStr( pPrinterName );
  72. pSpool->pFullMachineName = AllocSplStr( pFullMachineName );
  73. pSpool->pSplMapView = NULL;
  74. pSpool->pMappedJob = NULL;
  75. if ( pSpool->pName == NULL ||
  76. ( pFullMachineName && !pSpool->pFullMachineName )) {
  77. leave;
  78. }
  79. pSpool->pIniSpooler = pIniSpooler;
  80. #ifdef _HYDRA_
  81. pSpool->SessionId = GetClientSessionId();
  82. #endif
  83. //
  84. // Check if it's a local call.
  85. //
  86. if( TypeofHandle & PRINTER_HANDLE_REMOTE_CALL ) {
  87. //
  88. // We get other useful info like build #, client architecture
  89. // we do not need this info now -- so we do not put it in PSPOOL
  90. //
  91. if ( !pSplClientInfo ) {
  92. if ( IsNamedPipeRpcCall() )
  93. TypeofHandle |= PRINTER_HANDLE_3XCLIENT;
  94. } else if ( dwLevel == 1 ) {
  95. SPLASSERT(pSplClientInfo1->pUserName && pSplClientInfo1->pMachineName);
  96. CopyMemory(&pSpool->SplClientInfo1,
  97. pSplClientInfo1,
  98. sizeof(SPLCLIENT_INFO_1));
  99. pSpool->SplClientInfo1.pUserName = AllocSplStr(pSplClientInfo1->pUserName);
  100. pSpool->SplClientInfo1.pMachineName = AllocSplStr(pSplClientInfo1->pMachineName);
  101. if ( !pSpool->SplClientInfo1.pUserName ||
  102. !pSpool->SplClientInfo1.pMachineName ) {
  103. DBGMSG(DBG_WARNING, ("CreatePrinterHandle: could not allocate memory for user name or machine name\n"));
  104. }
  105. }
  106. }
  107. if ((TypeofHandle & PRINTER_HANDLE_SERVER) ||
  108. (TypeofHandle & PRINTER_HANDLE_XCV_PORT)) {
  109. bStatus = ValidateObjectAccess( SPOOLER_OBJECT_SERVER,
  110. DesiredAccess,
  111. pSpool,
  112. &pSpool->GrantedAccess,
  113. pIniSpooler );
  114. if ( bStatus &&
  115. (TypeofHandle & PRINTER_HANDLE_REMOTE_CALL) &&
  116. ( (DesiredAccess & SERVER_ACCESS_ADMINISTER) &&
  117. ValidateObjectAccess(SPOOLER_OBJECT_SERVER,
  118. SERVER_ACCESS_ADMINISTER,
  119. NULL,
  120. NULL,
  121. pIniSpooler)) ){
  122. pSpool->TypeofHandle |= PRINTER_HANDLE_REMOTE_ADMIN;
  123. }
  124. ObjectType = SPOOLER_OBJECT_SERVER;
  125. } else if( TypeofHandle & PRINTER_HANDLE_JOB ){
  126. bStatus = ValidateObjectAccess( SPOOLER_OBJECT_DOCUMENT,
  127. DesiredAccess,
  128. pSpool->pIniJob,
  129. &pSpool->GrantedAccess,
  130. pIniSpooler );
  131. ObjectType = SPOOLER_OBJECT_DOCUMENT;
  132. } else {
  133. bStatus = ValidateObjectAccess( SPOOLER_OBJECT_PRINTER,
  134. DesiredAccess,
  135. pSpool,
  136. &pSpool->GrantedAccess,
  137. pIniSpooler );
  138. ObjectType = SPOOLER_OBJECT_PRINTER;
  139. }
  140. MapGenericToSpecificAccess( ObjectType,
  141. pSpool->GrantedAccess,
  142. &pSpool->GrantedAccess);
  143. if ( !bStatus ) {
  144. SetLastError(ERROR_ACCESS_DENIED);
  145. leave;
  146. }
  147. if ( pIniPrinter ) {
  148. if ( pDefaults ) {
  149. //
  150. // Allocate DevMode
  151. //
  152. if ( pDefaults->pDevMode ) {
  153. pDevMode = pDefaults->pDevMode;
  154. } else {
  155. pDevMode = pIniPrinter->pDevMode;
  156. }
  157. if ( pDevMode != NULL ) {
  158. pSpool->pDevMode = AllocSplMem( pDevMode->dmSize + pDevMode->dmDriverExtra );
  159. if ( pSpool->pDevMode == NULL ) {
  160. DBGMSG(DBG_WARNING, ("CreatePrinterHandle failed allocation for devmode %d\n", GetLastError() ));
  161. leave;
  162. }
  163. memcpy( pSpool->pDevMode, pDevMode, pDevMode->dmSize + pDevMode->dmDriverExtra );
  164. }
  165. }
  166. //
  167. // Allocate Datype and Print Processor
  168. //
  169. if ( pDefaults && pDefaults->pDatatype ) {
  170. pSpool->pDatatype = AllocSplStr( pDefaults->pDatatype );
  171. pSpool->pIniPrintProc = FindDatatype( pIniPrinter->pIniPrintProc, pSpool->pDatatype );
  172. } else {
  173. pSpool->pDatatype = AllocSplStr( pIniPrinter->pDatatype );
  174. pSpool->pIniPrintProc = pIniPrinter->pIniPrintProc;
  175. }
  176. if ( pSpool->pIniPrintProc == NULL ) {
  177. DBGMSG( DBG_WARNING,("CreatePrinterHandle failed to PrintProcessor for datatype %ws %d\n",
  178. pSpool->pDatatype, GetLastError() ));
  179. SetLastError( ERROR_INVALID_DATATYPE );
  180. leave;
  181. }
  182. SPLASSERT( pSpool->pIniPrintProc->signature == IPP_SIGNATURE );
  183. pSpool->pIniPrintProc->cRef++;
  184. if ( pSpool->pDatatype == NULL ) {
  185. DBGMSG( DBG_WARNING,("CreatePrinterHandle failed to allocate DataType %x\n", GetLastError() ));
  186. SetLastError( ERROR_INVALID_DATATYPE );
  187. leave;
  188. }
  189. }
  190. // Add us to the linked list of handles for this printer.
  191. // This will be scanned when a change occurs on the printer,
  192. // and will be updated with a flag indicating what type of
  193. // change it was.
  194. // There is a flag for each handle, because we cannot guarantee
  195. // that all threads will have time to reference a flag in the
  196. // INIPRINTER before it is updated.
  197. if ( TypeofHandle & PRINTER_HANDLE_PRINTER ) {
  198. pSpool->pNext = pSpool->pIniPrinter->pSpool;
  199. pSpool->pIniPrinter->pSpool = pSpool;
  200. } else if ( (TypeofHandle & PRINTER_HANDLE_SERVER) ||
  201. (TypeofHandle & PRINTER_HANDLE_XCV_PORT) ) {
  202. //
  203. // For server handles, hang them off the global IniSpooler:
  204. //
  205. pSpool->pNext = pIniSpooler->pSpool;
  206. pIniSpooler->pSpool = pSpool;
  207. INCSPOOLERREF( pIniSpooler );
  208. } else if( TypeofHandle & PRINTER_HANDLE_JOB ){
  209. INCJOBREF( pIniJob );
  210. }
  211. // Note Only PRINTER_HANDLE_PRINTER are attatched to the
  212. // pIniPrinter, since those are the handle which will require
  213. // change notifications.
  214. if ( pSpool->pIniPrinter != NULL ) {
  215. INCPRINTERREF( pSpool->pIniPrinter );
  216. }
  217. hReturnHandle = (HANDLE)pSpool;
  218. } finally {
  219. if ( hReturnHandle == NULL ) {
  220. // Failure CleanUP
  221. if ( pSpool != NULL ) {
  222. FreeSplStr(pSpool->SplClientInfo1.pUserName);
  223. FreeSplStr(pSpool->SplClientInfo1.pMachineName);
  224. FreeSplStr( pSpool->pName ) ;
  225. FreeSplStr( pSpool->pDatatype );
  226. FreeSplStr(pSpool->pFullMachineName);
  227. if ( pSpool->pIniPrintProc != NULL )
  228. pSpool->pIniPrintProc->cRef--;
  229. if ( pSpool->pDevMode )
  230. FreeSplMem( pSpool->pDevMode );
  231. FreeSplMem( pSpool );
  232. pSpool = NULL;
  233. }
  234. }
  235. }
  236. return hReturnHandle;
  237. }
  238. BOOL
  239. DeletePrinterHandle(
  240. PSPOOL pSpool
  241. )
  242. {
  243. BOOL bRet = FALSE;
  244. SplInSem();
  245. if (pSpool->pIniPrintProc) {
  246. pSpool->pIniPrintProc->cRef--;
  247. }
  248. if (pSpool->pDevMode)
  249. FreeSplMem(pSpool->pDevMode);
  250. FreeSplStr(pSpool->SplClientInfo1.pUserName);
  251. FreeSplStr(pSpool->SplClientInfo1.pMachineName);
  252. FreeSplStr(pSpool->pDatatype);
  253. SetSpoolClosingChange(pSpool);
  254. FreeSplStr(pSpool->pName);
  255. FreeSplStr(pSpool->pFullMachineName);
  256. bRet = ObjectCloseAuditAlarm( szSpooler, pSpool, pSpool->GenerateOnClose );
  257. //
  258. // If there is a WaitForPrinterChange outstanding, we can't free
  259. // the pSpool, since we may try and reference it.
  260. //
  261. // Log warning for freed printer handle
  262. DBGMSG(DBG_TRACE, ("DeletePrinterHandle 0x%x", pSpool));
  263. if (pSpool->ChangeEvent) {
  264. pSpool->eStatus |= STATUS_PENDING_DELETION;
  265. } else {
  266. FreeSplMem(pSpool);
  267. }
  268. return TRUE;
  269. }
  270. DWORD
  271. CreateServerHandle(
  272. LPWSTR pPrinterName,
  273. LPHANDLE pPrinterHandle,
  274. LPPRINTER_DEFAULTS pDefaults,
  275. PINISPOOLER pIniSpooler,
  276. DWORD dwTypeofHandle
  277. )
  278. {
  279. DWORD DesiredAccess;
  280. DWORD ReturnValue = ROUTER_STOP_ROUTING;
  281. DBGMSG(DBG_TRACE, ("OpenPrinter(%ws)\n",
  282. pPrinterName ? pPrinterName : L"NULL"));
  283. EnterSplSem();
  284. if (!pDefaults || !pDefaults->DesiredAccess)
  285. DesiredAccess = SERVER_READ;
  286. else
  287. DesiredAccess = pDefaults->DesiredAccess;
  288. if (*pPrinterHandle = CreatePrinterHandle( pIniSpooler->pMachineName,
  289. pPrinterName,
  290. NULL, NULL, NULL, NULL,
  291. dwTypeofHandle,
  292. NULL,
  293. pDefaults,
  294. pIniSpooler,
  295. DesiredAccess,
  296. NULL,
  297. 0,
  298. INVALID_HANDLE_VALUE )){
  299. ReturnValue = ROUTER_SUCCESS;
  300. }
  301. LeaveSplSem();
  302. DBGMSG(DBG_TRACE, ("OpenPrinter returned handle %08x\n", *pPrinterHandle));
  303. return ReturnValue;
  304. }
  305. PINIPRINTER
  306. FindPrinterShare(
  307. LPCWSTR pszShareName,
  308. PINISPOOLER pIniSpooler
  309. )
  310. /*++
  311. Routine Description:
  312. Try and find the share name in our list of printers.
  313. Note: Even if the printer isn't shared, we still return a match.
  314. The caching code will work because it explicitly turns off
  315. the PRINTER_ATTRIBUTE_SHARE bit so that the cache pIniSpooler
  316. doesn't create a server thread or call NetShareAdd/Del.
  317. In the future, consider changing this to check the share bit.
  318. Create a new bit SPL_SHARE_PRINTERS that indicates whether sharing
  319. housekeeping should be done.
  320. Arguments:
  321. pszShareName - Name of share to search for.
  322. Return Value:
  323. PINIPRINTER Printer that has the share name, NULL if no printer.
  324. --*/
  325. {
  326. PINIPRINTER pIniPrinter;
  327. SplInSem();
  328. if (pszShareName && pszShareName[0]) {
  329. for( pIniPrinter = pIniSpooler->pIniPrinter;
  330. pIniPrinter;
  331. pIniPrinter = pIniPrinter->pNext ){
  332. if (pIniPrinter->pShareName &&
  333. !lstrcmpi(pIniPrinter->pShareName, pszShareName)) {
  334. return pIniPrinter;
  335. }
  336. }
  337. }
  338. return NULL;
  339. }
  340. PINISPOOLER
  341. LocalFindSpoolerByNameIncRef(
  342. LPWSTR pszPrinterName,
  343. LPTSTR *ppszLocalName OPTIONAL
  344. )
  345. /*++
  346. Routine Description:
  347. See if the printer is owned by localspl. This is a special
  348. case to check if it's a masquarading printer.
  349. Normally we would check for \\Server\Printer to see if \\Server
  350. is our machine, but we have to look for \\MasqServer\Printer
  351. too.
  352. Arguments:
  353. pPrinterName - Name to check.
  354. ppszLocalName - Returns pointer to local name. OPTIONAL
  355. Return Value:
  356. PINISPOOLER - Spooler match.
  357. NULL - No match.
  358. --*/
  359. {
  360. PINISPOOLER pIniSpooler;
  361. LPWSTR pTemp;
  362. if (!ppszLocalName)
  363. ppszLocalName = &pTemp;
  364. EnterSplSem();
  365. pIniSpooler = FindSpoolerByName( pszPrinterName,
  366. ppszLocalName );
  367. if( !pIniSpooler ){
  368. //
  369. // Check if it's a masq printer.
  370. //
  371. // If the local name isn't the same as the original name, it
  372. // is in the syntax "\\server\printer." In this case it may
  373. // be a masq printer, so check if the printer exists in the
  374. // local spooler by this name.
  375. //
  376. if( *ppszLocalName != pszPrinterName ){
  377. //
  378. // Search for the printer, but remove any suffixes it
  379. // may have.
  380. //
  381. WCHAR string[MAX_UNC_PRINTER_NAME + PRINTER_NAME_SUFFIX_MAX];
  382. wcscpy(string, pszPrinterName);
  383. if( pTemp = wcschr( string, L',' )){
  384. *pTemp = 0;
  385. }
  386. if( FindPrinter( string, pLocalIniSpooler )){
  387. //
  388. // The masq printer exists. The local name for this
  389. // masq printer is "\\MasqServer\Printer," so we must
  390. // reflect this change in ppszLocalName. This will ensure
  391. // that the pIniPrinter is found.
  392. //
  393. *ppszLocalName = pszPrinterName;
  394. pIniSpooler = pLocalIniSpooler;
  395. }
  396. }
  397. }
  398. if( pIniSpooler ){
  399. INCSPOOLERREF( pIniSpooler );
  400. }
  401. LeaveSplSem();
  402. return pIniSpooler;
  403. }
  404. VOID
  405. LocalFindSpoolerByNameDecRef(
  406. PINISPOOLER pIniSpooler
  407. )
  408. /*++
  409. Routine Description:
  410. Matching call to LocalFindSpoolerByNameIncRef.
  411. Arguments:
  412. pIniSpooler - Spooler to derement; can be NULL.
  413. Return Value:
  414. --*/
  415. {
  416. EnterSplSem();
  417. if( pIniSpooler ){
  418. DECSPOOLERREF( pIniSpooler );
  419. }
  420. LeaveSplSem();
  421. }
  422. DWORD
  423. LocalOpenPrinter(
  424. LPWSTR pPrinterName,
  425. LPHANDLE pPrinterHandle,
  426. LPPRINTER_DEFAULTS pDefaults
  427. )
  428. {
  429. return LocalOpenPrinterEx( pPrinterName,
  430. pPrinterHandle,
  431. pDefaults,
  432. NULL,
  433. 0 );
  434. }
  435. DWORD
  436. LocalOpenPrinterEx(
  437. LPWSTR pPrinterName,
  438. LPHANDLE pPrinterHandle,
  439. LPPRINTER_DEFAULTS pDefaults,
  440. LPBYTE pSplClientInfo,
  441. DWORD dwLevel
  442. )
  443. {
  444. DWORD dwReturn;
  445. LPWSTR pszLocalName;
  446. PINISPOOLER pIniSpooler = LocalFindSpoolerByNameIncRef( pPrinterName,
  447. &pszLocalName);
  448. //
  449. // WMI Trace Event.
  450. //
  451. LogWmiTraceEvent(0, EVENT_TRACE_TYPE_SPL_ENDTRACKTHREAD, NULL);
  452. if( !pIniSpooler ) {
  453. //
  454. // Check for PrinterName,LocalsplOnly.
  455. // If we see this token, then fail the call since
  456. // we only want to check localspl.
  457. //
  458. LPCTSTR pSecondPart;
  459. if( pSecondPart = wcschr( pPrinterName, L',' )){
  460. ++pSecondPart;
  461. if( wcscmp( pSecondPart, pszLocalsplOnlyToken ) == STRINGS_ARE_EQUAL ){
  462. SetLastError( ERROR_INVALID_PRINTER_NAME );
  463. return ROUTER_STOP_ROUTING;
  464. }
  465. }
  466. SetLastError( ERROR_INVALID_NAME );
  467. return ROUTER_UNKNOWN;
  468. }
  469. dwReturn = SplOpenPrinter( pPrinterName,
  470. pPrinterHandle,
  471. pDefaults,
  472. pIniSpooler,
  473. pSplClientInfo,
  474. dwLevel);
  475. LocalFindSpoolerByNameDecRef( pIniSpooler );
  476. //
  477. // We need to give other provider a chance to get the printer name
  478. //
  479. #if 0
  480. //
  481. // If we didn't find a printer but we did find a pIniSpooler and
  482. // the printer name was a remote name (e.g., "\\localmachine\printer"),
  483. // then we should stop routing. In this case, the local machine owns
  484. // the pIniSpooler, but it didn't find any printers. Don't bother
  485. // checking the other providers.
  486. //
  487. // In theory, we should be able to quit for local printers too
  488. // (e.g., OpenPrinter( "UnknownPrinter", ... )), but if any other
  489. // provider expects to get this name, they no longer will.
  490. //
  491. if( dwReturn == ROUTER_UNKNOWN &&
  492. pszLocalName != pPrinterName ){
  493. dwReturn = ROUTER_STOP_ROUTING;
  494. }
  495. #endif
  496. return dwReturn;
  497. }
  498. DWORD
  499. OpenLocalPrinterName(
  500. LPCWSTR pPrinterName,
  501. PINISPOOLER pIniSpooler,
  502. PDWORD pTypeofHandle,
  503. PINIPRINTER* ppIniPrinter,
  504. PINIPORT* ppIniPort,
  505. PINIPORT* ppIniNetPort,
  506. PHANDLE phPort,
  507. PDWORD pOpenPortError,
  508. LPPRINTER_DEFAULTS pDefaults
  509. )
  510. {
  511. PINIPRINTER pIniPrinter;
  512. PINIPORT pIniPort;
  513. PINIPORT pIniNetPort = NULL;
  514. BOOL bOpenPrinterPort;
  515. LPWSTR pDatatype;
  516. //
  517. // If the printer name is the name of a local printer:
  518. //
  519. // Find the first port the printer's attached to.
  520. //
  521. // If the port has a monitor (e.g. LPT1:, COM1 etc.),
  522. // we're OK,
  523. // Otherwise
  524. // try to open the port - this may be a network printer
  525. //
  526. if( ( pIniPrinter = FindPrinter( pPrinterName, pIniSpooler )) ||
  527. ( pIniPrinter = FindPrinterShare( pPrinterName, pIniSpooler ))) {
  528. pIniPort = FindIniPortFromIniPrinter( pIniPrinter );
  529. if( pIniPort && ( pIniPort->Status & PP_MONITOR )){
  530. //
  531. // A Printer that has a Port with a Monitor is not a
  532. // DownLevel Connection (or LocalPrinter acting as a
  533. // remote printer - "Masquarade" case).
  534. //
  535. pIniPort = NULL;
  536. }
  537. pDatatype = (pDefaults && pDefaults->pDatatype) ?
  538. pDefaults->pDatatype :
  539. NULL;
  540. //
  541. // Validate datatypes for both masq and local.
  542. //
  543. if( pDatatype && !FindDatatype( NULL, pDatatype )){
  544. goto BadDatatype;
  545. }
  546. if( pIniPort ){
  547. //
  548. // DownLevel Connection Printer; save it in pIniNetPort.
  549. // SetPrinterPorts checks this value.
  550. //
  551. pIniNetPort = pIniPort;
  552. //
  553. // Validate datatype. We only send RAW across the net
  554. // to masq printers.
  555. //
  556. if( pDatatype && !ValidRawDatatype( pDatatype )){
  557. goto BadDatatype;
  558. }
  559. //
  560. // There is a network port associated with this printer.
  561. // Make sure we can open it, and get the handle to use on
  562. // future API calls:
  563. //
  564. INCPRINTERREF(pIniPrinter);
  565. LeaveSplSem();
  566. bOpenPrinterPort = OpenPrinterPortW( pIniPort->pName, phPort, pDefaults );
  567. EnterSplSem();
  568. DECPRINTERREF(pIniPrinter);
  569. if( !bOpenPrinterPort ){
  570. *phPort = INVALID_PORT_HANDLE;
  571. *pOpenPortError = GetLastError();
  572. //
  573. // Must be non-zero otherwise it looks like success.
  574. //
  575. SPLASSERT( *pOpenPortError );
  576. if( *pOpenPortError == ERROR_INVALID_PASSWORD ) {
  577. //
  578. // This call should fail if it's because the password
  579. // is invalid, then winspool or printman can prompt
  580. // for the password.
  581. //
  582. DBGMSG(DBG_WARNING, ("OpenPrinterPort1( %ws ) failed with ERROR_INVALID_PASSWORD . OpenPrinter returning FALSE\n", pIniPort->pName ));
  583. return ROUTER_STOP_ROUTING;
  584. }
  585. DBGMSG(DBG_WARNING, ("OpenPrinterPort1( %ws ) failed: Error %d. OpenPrinter returning TRUE\n", pIniPort->pName, *pOpenPortError));
  586. } else {
  587. //
  588. // Clear the placeholder bit from the pIniPort status. This
  589. // belongs to a partial print provider.
  590. //
  591. pIniPort->Status &= ~PP_PLACEHOLDER;
  592. }
  593. } else {
  594. //
  595. // Not a masq case. If it's direct, it must be raw.
  596. //
  597. // Note: we will use the default if no datatype is specified.
  598. // However, if the default datatype is non-RAW and the
  599. // printer is direct, the open will succeed using a
  600. // non-RAW datatype!
  601. //
  602. if(( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_DIRECT ) &&
  603. pDatatype &&
  604. !ValidRawDatatype( pDatatype )) {
  605. goto BadDatatype;
  606. }
  607. }
  608. //
  609. // If this is a placeholder port, assume that it is a monitor port for now.
  610. //
  611. if (pIniPort && pIniPort->Status & PP_PLACEHOLDER) {
  612. pIniPort = NULL;
  613. pIniNetPort = NULL;
  614. }
  615. *pTypeofHandle |= ( pIniPort ?
  616. PRINTER_HANDLE_PORT :
  617. PRINTER_HANDLE_PRINTER );
  618. *ppIniPort = pIniPort;
  619. *ppIniNetPort = pIniNetPort;
  620. *ppIniPrinter = pIniPrinter;
  621. return ROUTER_SUCCESS;
  622. }
  623. SetLastError( ERROR_INVALID_NAME );
  624. return ROUTER_UNKNOWN;
  625. BadDatatype:
  626. SetLastError( ERROR_INVALID_DATATYPE );
  627. return ROUTER_STOP_ROUTING;
  628. }
  629. DWORD
  630. CheckPrinterTokens(
  631. LPCWSTR string,
  632. LPCWSTR pSecondPart,
  633. PDWORD pTypeofHandle,
  634. PINISPOOLER pIniSpooler,
  635. PINIPRINTER *ppIniPrinter,
  636. PINIPORT *ppIniPort,
  637. PINIPORT *ppIniNetPort,
  638. PHANDLE phPort,
  639. PDWORD pOpenPortError,
  640. PPRINTER_DEFAULTS pDefaults
  641. )
  642. {
  643. typedef enum {
  644. kNone = 0,
  645. kLocalOnly = 1,
  646. kLocalsplOnly = 2
  647. } ETOKEN_TYPE;
  648. ETOKEN_TYPE eTokenType = kNone;
  649. DWORD RouterReturnValue = ROUTER_UNKNOWN;
  650. //
  651. // LocalOnly
  652. //
  653. // Do not call OpenPrinterPort--use local settings only.
  654. // If not recognized by localspl, stop routing.
  655. //
  656. // This is used during upgrade of a downlevel printer connection.
  657. // The remote server may not be up, but we can return a print
  658. // handle to the local printer. Get/SetPrinterData calls will
  659. // succeed (for upgrade purposes), but printing will fail.
  660. //
  661. // LocalsplOnly
  662. //
  663. // Call OpenPrinterPort if necessary.
  664. // Stop routing after localspl even if not found.
  665. //
  666. // This is used when the system knows that the printer must exist
  667. // on the local machine, and does not want to route further.
  668. // This fixes the clustering problem when the server has a stale
  669. // print share and successfully validates it against win32spl since
  670. // it is cached.
  671. //
  672. if( wcsncmp( pSecondPart, pszLocalOnlyToken, wcslen(pszLocalOnlyToken) ) == STRINGS_ARE_EQUAL ){
  673. eTokenType = kLocalOnly;
  674. } else if( wcsncmp( pSecondPart, pszLocalsplOnlyToken, wcslen(pszLocalsplOnlyToken) ) == STRINGS_ARE_EQUAL ){
  675. eTokenType = kLocalsplOnly;
  676. }
  677. //
  678. // If we have a valid token, process it.
  679. //
  680. if( eTokenType != kNone ){
  681. switch( eTokenType ){
  682. case kLocalOnly:
  683. //
  684. // Find the printer associate with it.
  685. //
  686. *ppIniPrinter = FindPrinter( string, pIniSpooler );
  687. if( *ppIniPrinter ){
  688. *pTypeofHandle |= PRINTER_HANDLE_PRINTER;
  689. RouterReturnValue = ROUTER_SUCCESS;
  690. } else {
  691. RouterReturnValue = ROUTER_STOP_ROUTING;
  692. }
  693. break;
  694. case kLocalsplOnly:
  695. RouterReturnValue = OpenLocalPrinterName( string,
  696. pIniSpooler,
  697. pTypeofHandle,
  698. ppIniPrinter,
  699. ppIniPort,
  700. ppIniNetPort,
  701. phPort,
  702. pOpenPortError,
  703. pDefaults );
  704. *pTypeofHandle = *pTypeofHandle & (~PRINTER_HANDLE_REMOTE_CALL);
  705. if( RouterReturnValue == ROUTER_UNKNOWN ){
  706. RouterReturnValue = ROUTER_STOP_ROUTING;
  707. }
  708. }
  709. }
  710. DBGMSG( DBG_TRACE,
  711. ( "CheckPrinterTokens: %ws %d Requested %d %x\n",
  712. string, RouterReturnValue, *ppIniPrinter ));
  713. return RouterReturnValue;
  714. }
  715. DWORD
  716. CheckPrinterPortToken(
  717. LPCWSTR string,
  718. LPCWSTR pSecondPart,
  719. PDWORD pTypeofHandle,
  720. PINIPRINTER* ppIniPrinter,
  721. PINIPORT* ppIniPort,
  722. PINIJOB* ppIniJob,
  723. const LPPRINTER_DEFAULTS pDefaults,
  724. const PINISPOOLER pIniSpooler
  725. )
  726. {
  727. if( wcsncmp( pSecondPart, L"Port", 4 ) != STRINGS_ARE_EQUAL ||
  728. !( *ppIniPort = FindPort( string, pIniSpooler ))){
  729. return ROUTER_UNKNOWN;
  730. }
  731. //
  732. // The name is the name of a port:
  733. //
  734. if( pDefaults &&
  735. pDefaults->pDatatype &&
  736. !ValidRawDatatype( pDefaults->pDatatype )) {
  737. SetLastError( ERROR_INVALID_DATATYPE );
  738. return ROUTER_STOP_ROUTING;
  739. }
  740. if ( *ppIniJob = (*ppIniPort)->pIniJob ) {
  741. *ppIniPrinter = (*ppIniJob)->pIniPrinter;
  742. *pTypeofHandle |= PRINTER_HANDLE_PORT;
  743. } else if( (*ppIniPort)->cPrinters ){
  744. //
  745. // There is no current job assigned to the port
  746. // So Open the First Printer Associated with
  747. // this port.
  748. //
  749. *ppIniPrinter = (*ppIniPort)->ppIniPrinter[0];
  750. *pTypeofHandle |= PRINTER_HANDLE_PRINTER;
  751. }
  752. return ROUTER_SUCCESS;
  753. }
  754. DWORD
  755. CheckPrinterJobToken(
  756. LPCWSTR string,
  757. LPCWSTR pSecondPart,
  758. PDWORD pTypeofHandle,
  759. PINIPRINTER* ppIniPrinter,
  760. PINIJOB* ppIniJob,
  761. PHANDLE phReadFile,
  762. const PINISPOOLER pIniSpooler
  763. )
  764. {
  765. HANDLE hImpersonationToken;
  766. DWORD Position, dwShareMode, dwDesiredAccess;
  767. DWORD JobId;
  768. PINIPRINTER pIniPrinter;
  769. PINIJOB pIniJob, pCurrentIniJob;
  770. PWSTR pszStr = NULL;
  771. if( wcsncmp( pSecondPart, L"Job ", 4 ) != STRINGS_ARE_EQUAL ||
  772. !( pIniPrinter = FindPrinter( string, pIniSpooler ))){
  773. return ROUTER_UNKNOWN;
  774. }
  775. //
  776. // Get the Job ID ",Job xxxx"
  777. //
  778. pSecondPart += 4;
  779. JobId = Myatol( (LPWSTR)pSecondPart );
  780. pIniJob = FindJob( pIniPrinter, JobId, &Position );
  781. if( pIniJob == NULL ) {
  782. DBGMSG( DBG_WARN, ("OpenPrinter failed to find Job %d\n", JobId ));
  783. return ROUTER_UNKNOWN;
  784. }
  785. DBGMSG( DBG_TRACE, ("OpenPrinter: pIniJob->cRef = %d\n", pIniJob->cRef));
  786. if( pIniJob->Status & JOB_DIRECT ) {
  787. SplInSem();
  788. *pTypeofHandle |= PRINTER_HANDLE_JOB | PRINTER_HANDLE_DIRECT;
  789. goto Success;
  790. }
  791. //
  792. // If this job is assigned to a port
  793. // Then pick up the correct chained jobid file instead of the master
  794. // JobId.
  795. //
  796. if ( pIniJob->pCurrentIniJob != NULL ) {
  797. SPLASSERT( pIniJob->pCurrentIniJob->signature == IJ_SIGNATURE );
  798. DBGMSG( DBG_TRACE,("CheckPrinterJobToken pIniJob %x JobId %d using chain JobId %d\n",
  799. pIniJob, pIniJob->JobId, pIniJob->pCurrentIniJob->JobId ));
  800. pCurrentIniJob = pIniJob->pCurrentIniJob;
  801. SPLASSERT( pCurrentIniJob->signature == IJ_SIGNATURE );
  802. } else {
  803. pCurrentIniJob = pIniJob;
  804. }
  805. if ( pCurrentIniJob->hFileItem != INVALID_HANDLE_VALUE )
  806. {
  807. LeaveSplSem();
  808. hImpersonationToken = RevertToPrinterSelf();
  809. GetReaderFromHandle(pCurrentIniJob->hFileItem, phReadFile);
  810. ImpersonatePrinterClient( hImpersonationToken );
  811. if ( *phReadFile && (*phReadFile != INVALID_HANDLE_VALUE))
  812. {
  813. GetNameFromHandle(pCurrentIniJob->hFileItem, &pszStr, TRUE);
  814. wcscpy((LPWSTR)string, pszStr);
  815. FreeSplStr(pszStr);
  816. EnterSplSem();
  817. *pTypeofHandle |= PRINTER_HANDLE_JOB;
  818. goto Success;
  819. }
  820. DBGMSG( DBG_WARN,("Filepools: Failed to get valid reader handle\n"));
  821. EnterSplSem();
  822. }
  823. else
  824. {
  825. GetFullNameFromId( pCurrentIniJob->pIniPrinter,
  826. pCurrentIniJob->JobId,
  827. TRUE,
  828. (LPWSTR)string,
  829. FALSE );
  830. // Bug 54845
  831. // Even a user without previledge can open a ", JOB #"
  832. // if he is physically running on the machine.
  833. LeaveSplSem();
  834. hImpersonationToken = RevertToPrinterSelf();
  835. dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
  836. if (pCurrentIniJob->Status & JOB_TYPE_OPTIMIZE) {
  837. dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
  838. } else {
  839. dwDesiredAccess = GENERIC_READ;
  840. }
  841. *phReadFile = CreateFile(string,
  842. dwDesiredAccess,
  843. dwShareMode,
  844. NULL,
  845. OPEN_EXISTING,
  846. FILE_ATTRIBUTE_NORMAL,
  847. NULL);
  848. ImpersonatePrinterClient( hImpersonationToken );
  849. EnterSplSem();
  850. if( *phReadFile != INVALID_HANDLE_VALUE ) {
  851. DBGMSG( DBG_TRACE,
  852. ( "OpenPrinter JobID %d pIniJob %x CreateFile( %ws ), hReadFile %x success",
  853. JobId, pIniJob, string, *phReadFile ));
  854. SplInSem();
  855. *pTypeofHandle |= PRINTER_HANDLE_JOB;
  856. goto Success;
  857. }
  858. }
  859. DBGMSG( DBG_WARNING,
  860. ( "LocalOpenPrinter CreateFile(%ws) GENERIC_READ failed : %d\n",
  861. string, GetLastError()));
  862. SPLASSERT( GetLastError( ));
  863. return ROUTER_STOP_ROUTING;
  864. Success:
  865. *ppIniJob = pIniJob;
  866. *ppIniPrinter = pIniPrinter;
  867. return ROUTER_SUCCESS;
  868. }
  869. DWORD
  870. CheckXcvPortToken(
  871. LPCWSTR pszSecondPart,
  872. PDWORD pTypeofHandle,
  873. const LPPRINTER_DEFAULTS pDefaults,
  874. const PINISPOOLER pIniSpooler,
  875. PHANDLE phXcv
  876. )
  877. {
  878. DWORD dwRet = ROUTER_SUCCESS;
  879. DWORD dwType;
  880. PCWSTR pszPort;
  881. DWORD dwTypeofHandle = *pTypeofHandle;
  882. if (!wcsncmp(pszSecondPart, SZXCVPORT, COUNTOF(SZXCVPORT) - 1)) {
  883. dwType = XCVPORT;
  884. dwTypeofHandle |= PRINTER_HANDLE_XCV_PORT;
  885. pszPort = (PCWSTR) pszSecondPart + COUNTOF(SZXCVPORT) - 1;
  886. }
  887. else if (!wcsncmp(pszSecondPart, SZXCVMONITOR, COUNTOF(SZXCVMONITOR) - 1)) {
  888. dwType = XCVMONITOR;
  889. dwTypeofHandle |= PRINTER_HANDLE_XCV_PORT;
  890. pszPort = (PCWSTR) pszSecondPart + COUNTOF(SZXCVMONITOR) - 1;
  891. }
  892. else
  893. dwRet = ROUTER_UNKNOWN;
  894. if (dwRet == ROUTER_SUCCESS) {
  895. dwRet = XcvOpen(NULL,
  896. pszPort,
  897. dwType,
  898. pDefaults,
  899. phXcv,
  900. pIniSpooler);
  901. if (dwRet == ROUTER_SUCCESS)
  902. *pTypeofHandle = dwTypeofHandle;
  903. }
  904. return dwRet;
  905. }
  906. DWORD
  907. SplOpenPrinter(
  908. LPWSTR pFullPrinterName,
  909. LPHANDLE pPrinterHandle,
  910. LPPRINTER_DEFAULTS pDefaults,
  911. PINISPOOLER pIniSpooler,
  912. LPBYTE pSplClientInfo,
  913. DWORD dwLevel
  914. )
  915. /*++
  916. Routine Description:
  917. OpenPrinter can open any of the following by specifying a string
  918. in pPrinterName:-
  919. Server
  920. \\MachineName
  921. NULL
  922. Job
  923. PrinterName, Job xxxx
  924. Port
  925. PortName, Port
  926. XcvPort
  927. \\MachineName\,XcvPort Port
  928. ,XcvPort Port
  929. XcvMonitor
  930. \\MachineName\,XcvMonitor Monitor
  931. ,XcvMonitor Monitor
  932. Printer
  933. PrinterName
  934. ShareName
  935. \\MachineName\PrinterName
  936. \\MachineName\ShareName
  937. PrinterName, LocalOnly
  938. ShareName, LocalOnly
  939. PrinterName, LocalsplOnly
  940. ShareName, LocalsplOnly
  941. Note for Printer there are two Types
  942. 1 - Regular LocalPrinter
  943. 2 - DownLevel Connection Printer
  944. For type 2 a LocalPrinter exists ( pIniPrinter ) but its port
  945. does not have a monitor associated with it. In this case
  946. we also open the port ( typically \\share\printer of a remote
  947. machine ) before we return success.
  948. GUI Applications usually use Server and Printer
  949. Type Job and Port are used by Print Processors:-
  950. A print processor will Open a Job then read the job using
  951. ReadPrinter. A print processor will output to a Port by opening
  952. the PortName, Port and using WritePrinter. Usually these strings
  953. "PrinterName, Job xxx" "PortName, Port" are passed to the print
  954. processor by the spooler and are currently not documented. We
  955. do know that some OEMs have figured out the extentions and we
  956. might break someone if we change them.
  957. Type LocalOnlyToken is used by a Printer Driver:-
  958. Used when we need to upgrade a printer's settings from an older
  959. version of the driver to a newer one (see drvupgrd.c for details).
  960. This was added in NT 3.51.
  961. Type LocasplOnlyToken is used by server:-
  962. Indicates that we should check localspl only (local or masq).
  963. Other providers will not be called.
  964. Arguments:
  965. pPrinterName - PrinterName ( see above for different types of
  966. PrinterName )
  967. pPrinterHandle - Address to put hPrinter on Success
  968. pDefaults - Optional, allows user to specify Datatype,
  969. DevMode, DesiredAccess.
  970. pIniSpooler - This spooler "owns" the printer. We will only check
  971. against this spooler, and we assume that the callee
  972. has already checked that "\\server\printer" lives
  973. on this pIniSpooler (i.e., we are \\server).
  974. ( see SDK Online Help for full explanation )
  975. Return Value:
  976. TRUE - *pPrinterHandle will have a PrinterHandle
  977. FALSE - use GetLastError
  978. --*/
  979. {
  980. PINIPRINTER pIniPrinter = NULL;
  981. PINIPORT pIniPort = NULL;
  982. PINIPORT pIniNetPort = NULL;
  983. DWORD LastError = 0;
  984. LPWSTR pPrinterName = pFullPrinterName;
  985. WCHAR string[MAX_UNC_PRINTER_NAME + PRINTER_NAME_SUFFIX_MAX];
  986. PINIJOB pIniJob = NULL;
  987. HANDLE hReadFile = INVALID_HANDLE_VALUE;
  988. DWORD TypeofHandle = 0;
  989. LPWSTR pSecondPart = NULL;
  990. HANDLE hPort = INVALID_PORT_HANDLE;
  991. DWORD OpenPortError = NO_ERROR;
  992. BOOL bRemoteUserPrinterNotShared = FALSE;
  993. DWORD MachineNameLength;
  994. DWORD RouterReturnValue = ROUTER_UNKNOWN;
  995. DWORD DesiredAccess;
  996. LPTSTR pcMark;
  997. BOOL bRemoteNameRequest = FALSE;
  998. BOOL bLocalCall = FALSE;
  999. #if DBG
  1000. //
  1001. // On DBG builds, force last error to zero so we can catch people
  1002. // that don't set it when they should.
  1003. //
  1004. SetLastError( ERROR_SUCCESS );
  1005. #endif
  1006. //
  1007. // Reject "" - pointer to a NULL string.
  1008. //
  1009. if (pFullPrinterName && !pFullPrinterName[0]) {
  1010. SetLastError(ERROR_INVALID_NAME);
  1011. return ROUTER_UNKNOWN;
  1012. }
  1013. if (!pFullPrinterName) {
  1014. return CreateServerHandle( pFullPrinterName,
  1015. pPrinterHandle,
  1016. pDefaults,
  1017. pIniSpooler,
  1018. PRINTER_HANDLE_SERVER );
  1019. }
  1020. if( pFullPrinterName[0] == TEXT( '\\' ) && pFullPrinterName[1] == TEXT( '\\' )) {
  1021. wcscpy(string, pFullPrinterName);
  1022. if(pcMark = wcschr(string + 2, TEXT( '\\' ))) {
  1023. *pcMark = TEXT('\0');
  1024. }
  1025. if (MyName(string, pIniSpooler)) { // \\Server\Printer or \\Server
  1026. if (!pcMark) { // \\Server
  1027. return CreateServerHandle( pFullPrinterName,
  1028. pPrinterHandle,
  1029. pDefaults,
  1030. pIniSpooler,
  1031. PRINTER_HANDLE_SERVER );
  1032. }
  1033. // Have \\Server\Printer, Set pPrinterName = Printer
  1034. pPrinterName = pFullPrinterName + (pcMark - string) + 1;
  1035. bRemoteNameRequest = TRUE;
  1036. }
  1037. }
  1038. DBGMSG( DBG_TRACE, ( "OpenPrinter(%ws, %ws)\n", pFullPrinterName, pPrinterName ));
  1039. bLocalCall = IsLocalCall();
  1040. EnterSplSem();
  1041. //
  1042. // For the Mars folks who will come in with the same printer
  1043. // connection, do a DeletePrinterCheck; this will allow
  1044. // Mars connections that have been deleted to be proceed
  1045. // to the Mars print providor
  1046. //
  1047. if (( pIniPrinter = FindPrinter( pPrinterName, pIniSpooler )) ||
  1048. ( pIniPrinter = FindPrinterShare( pPrinterName, pIniSpooler ))) {
  1049. DeletePrinterCheck( pIniPrinter );
  1050. pIniPrinter = NULL;
  1051. }
  1052. //
  1053. // The strategy for the rest of this code is to walk through each
  1054. // different printer handle type, searching for a match.
  1055. //
  1056. // RouterReturnValue will be set to the current state of routing.
  1057. // If a section recognizes and "owns" a printer and successfully
  1058. // opens it, it sets RouterReturnValue to ROUTER_SUCCESS and
  1059. // jumps to DoneRouting which allocs the handle.
  1060. //
  1061. // If it recoginzes the printer but fails to open it, and
  1062. // guarentees that no one else (localspl code or other providers)
  1063. // will recognize it, it should set RouterReturnValue to
  1064. // ROUTER_STOP_ROUTING. We will quit at this point.
  1065. //
  1066. // If it doesn't recognize the printer, set RouterReturnValue
  1067. // to ROUTER_UNKNOWN and we will keep looking.
  1068. //
  1069. //
  1070. // Try regular printer name: "My Printer" "TestPrinter."
  1071. //
  1072. RouterReturnValue = OpenLocalPrinterName( pPrinterName,
  1073. pIniSpooler,
  1074. &TypeofHandle,
  1075. &pIniPrinter,
  1076. &pIniPort,
  1077. &pIniNetPort,
  1078. &hPort,
  1079. &OpenPortError,
  1080. pDefaults );
  1081. if( RouterReturnValue != ROUTER_UNKNOWN ){
  1082. if( bRemoteNameRequest ){
  1083. //
  1084. // On success, determine whether the user is remote or local.
  1085. // Note: we only do this for fully qualified names
  1086. // (\\server\share), since using just the share or printer
  1087. // name can only succeed locally.
  1088. //
  1089. if (bLocalCall) {
  1090. if( (pIniSpooler->SpoolerFlags & SPL_REMOTE_HANDLE_CHECK) &&
  1091. (pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER) )
  1092. TypeofHandle |= PRINTER_HANDLE_REMOTE_DATA;
  1093. } else {
  1094. if( pIniSpooler->SpoolerFlags & SPL_REMOTE_HANDLE_CHECK )
  1095. TypeofHandle |= PRINTER_HANDLE_REMOTE_DATA;
  1096. TypeofHandle |= PRINTER_HANDLE_REMOTE_CALL;
  1097. }
  1098. //
  1099. // This is a remote open.
  1100. //
  1101. // If the printer is not shared, ensure the caller
  1102. // has Administer access to the printer.
  1103. //
  1104. // The following seems to belong to the inside of the above "if"
  1105. // clause. As it is, if an interactive user calls in with UNC name,
  1106. // we require him to have ADMIN access if the printer is not shared;
  1107. // but if he uses the printer friendly name, we let him go.
  1108. //
  1109. if( pIniPrinter &&
  1110. !( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED )){
  1111. bRemoteUserPrinterNotShared = TRUE;
  1112. }
  1113. }
  1114. goto DoneRouting;
  1115. }
  1116. SPLASSERT( !TypeofHandle && !pIniPrinter && !pIniPort &&
  1117. !pIniNetPort && !pIniJob && !hPort );
  1118. //
  1119. // Try LocalPrinter with an extention e.g.
  1120. //
  1121. // PortName, Port
  1122. // PrinterName, Job xxxx
  1123. // PrinterName, LocalOnlyToken
  1124. // PrinterName, LocalsplOnlyToken
  1125. //
  1126. // See if the name includes a comma. Look for qualifiers:
  1127. // Port Job LocalOnly LocalsplOnly
  1128. //
  1129. wcscpy( string, pPrinterName );
  1130. if( pSecondPart = wcschr( string, L',' )){
  1131. DWORD dwError;
  1132. UINT uType;
  1133. //
  1134. // Turn into 2 strings
  1135. // First PrintName
  1136. // pSecondPart points to the rest.
  1137. //
  1138. *pSecondPart++ = 0;
  1139. //
  1140. // Get rid of Leading Spaces
  1141. //
  1142. while ( *pSecondPart == L' ' && *pSecondPart != 0 ) {
  1143. pSecondPart++;
  1144. }
  1145. SPLASSERT( *pSecondPart );
  1146. //
  1147. // PrintName, {LocalOnly|LocalsplOnly}
  1148. //
  1149. RouterReturnValue = CheckPrinterTokens( string,
  1150. pSecondPart,
  1151. &TypeofHandle,
  1152. pIniSpooler,
  1153. &pIniPrinter,
  1154. &pIniPort,
  1155. &pIniNetPort,
  1156. &hPort,
  1157. &OpenPortError,
  1158. pDefaults );
  1159. if( RouterReturnValue != ROUTER_UNKNOWN ){
  1160. goto DoneRouting;
  1161. }
  1162. SPLASSERT( !TypeofHandle && !pIniPrinter && !pIniPort &&
  1163. !pIniNetPort && !pIniJob && !hPort );
  1164. //
  1165. // PortName, Port
  1166. //
  1167. RouterReturnValue = CheckPrinterPortToken( string,
  1168. pSecondPart,
  1169. &TypeofHandle,
  1170. &pIniPrinter,
  1171. &pIniPort,
  1172. &pIniJob,
  1173. pDefaults,
  1174. pIniSpooler );
  1175. if( RouterReturnValue != ROUTER_UNKNOWN ){
  1176. goto DoneRouting;
  1177. }
  1178. SPLASSERT( !TypeofHandle && !pIniPrinter && !pIniPort &&
  1179. !pIniNetPort && !pIniJob && !hPort );
  1180. //
  1181. // PrinterName, Job ###
  1182. //
  1183. RouterReturnValue = CheckPrinterJobToken( string,
  1184. pSecondPart,
  1185. &TypeofHandle,
  1186. &pIniPrinter,
  1187. &pIniJob,
  1188. &hReadFile,
  1189. pIniSpooler );
  1190. if( RouterReturnValue != ROUTER_UNKNOWN ){
  1191. goto DoneRouting;
  1192. }
  1193. SPLASSERT( !TypeofHandle && !pIniPrinter && !pIniPort &&
  1194. !pIniNetPort && !pIniJob && !hPort );
  1195. //
  1196. // "\\Server\,XcvPort Object" or ",XcvPort Object"
  1197. // "\\Server\,XcvMonitor Object" or ",XcvMonitor Object"
  1198. //
  1199. // Verify that we're looking at the right server
  1200. if (bRemoteNameRequest || *pPrinterName == L',') {
  1201. RouterReturnValue = CheckXcvPortToken( pSecondPart,
  1202. &TypeofHandle,
  1203. pDefaults,
  1204. pIniSpooler,
  1205. pPrinterHandle );
  1206. } else {
  1207. RouterReturnValue = ROUTER_UNKNOWN;
  1208. }
  1209. goto WrapUp;
  1210. }
  1211. //
  1212. // We have completed all routing. Anything other than success
  1213. // should exit now.
  1214. //
  1215. DoneRouting:
  1216. if( RouterReturnValue == ROUTER_SUCCESS) {
  1217. //
  1218. // It's an error if the printer is pending deletion or pending creation.
  1219. //
  1220. SPLASSERT( pIniPrinter );
  1221. if (!pIniPrinter ||
  1222. (pIniPrinter->Status & PRINTER_PENDING_DELETION) &&
  1223. (pIniSpooler->SpoolerFlags & SPL_FAIL_OPEN_PRINTERS_PENDING_DELETION) &&
  1224. (pIniPrinter->cJobs == 0) ||
  1225. (pIniPrinter->Status & PRINTER_PENDING_CREATION)) {
  1226. RouterReturnValue = ROUTER_STOP_ROUTING;
  1227. SetLastError( ERROR_INVALID_PRINTER_NAME );
  1228. goto DoneRouting;
  1229. }
  1230. //
  1231. // When the printer is opened, access type may be specified in
  1232. // pDefaults. If no defaults are supplied (or request access
  1233. // is unspecified), we use PRINTER_ACCESS_USE.
  1234. //
  1235. // Future calls with the handle will check against both the
  1236. // current user privileges on this printer but also this initial
  1237. // access. (Even if the user is an admin of the printer, unless
  1238. // they open the printer with PRINTER_ALL_ACCESS, they can't
  1239. // administer it.)
  1240. //
  1241. // If the user requires more access, the printer must be reopened.
  1242. //
  1243. if( !pDefaults || !pDefaults->DesiredAccess ){
  1244. if( TypeofHandle & PRINTER_HANDLE_JOB ){
  1245. DesiredAccess = JOB_READ;
  1246. } else {
  1247. DesiredAccess = PRINTER_READ;
  1248. }
  1249. } else {
  1250. DesiredAccess = pDefaults->DesiredAccess;
  1251. }
  1252. //
  1253. // If the user is remote and the printer is not shared, only allow
  1254. // administrators succeed.
  1255. //
  1256. // This allows administrators to admin printers even if they
  1257. // are not shared, and prevents non-admins from opening non-shared
  1258. // printers.
  1259. //
  1260. if( bRemoteUserPrinterNotShared &&
  1261. !(DesiredAccess & PRINTER_ACCESS_ADMINISTER )) {
  1262. PSPOOL pSpool;
  1263. // Get a quick and dirty pSpool to pass in
  1264. pSpool = (PSPOOL)AllocSplMem( SPOOL_SIZE );
  1265. if( pSpool == NULL ) {
  1266. DBGMSG( DBG_WARNING, ("SplOpenPrinter failed to allocate memory %d\n", GetLastError() ));
  1267. RouterReturnValue = ROUTER_STOP_ROUTING;
  1268. goto WrapUp;
  1269. }
  1270. pSpool->signature = SJ_SIGNATURE;
  1271. pSpool->pIniPrinter = pIniPrinter;
  1272. // Add admin request, and see if user has the right.
  1273. DesiredAccess |= PRINTER_ACCESS_ADMINISTER;
  1274. if( !ValidateObjectAccess( SPOOLER_OBJECT_PRINTER,
  1275. DesiredAccess,
  1276. pSpool,
  1277. &pSpool->GrantedAccess,
  1278. pIniSpooler )) {
  1279. SetLastError(ERROR_ACCESS_DENIED);
  1280. RouterReturnValue = ROUTER_STOP_ROUTING;
  1281. }
  1282. DesiredAccess &= ~PRINTER_ACCESS_ADMINISTER;
  1283. // clean up
  1284. FreeSplMem( pSpool );
  1285. // If the user had no ADMIN privilege, fail the open call.
  1286. if( RouterReturnValue == ROUTER_STOP_ROUTING )
  1287. goto WrapUp;
  1288. }
  1289. //
  1290. // Create the printer handle that we will return to the user.
  1291. //
  1292. if( pFullPrinterName != pPrinterName) {
  1293. wcsncpy( string, pFullPrinterName, (size_t) (pPrinterName - pFullPrinterName - 1));
  1294. string[pPrinterName - pFullPrinterName - 1] = L'\0';
  1295. } else {
  1296. wcscpy(string, pIniSpooler->pMachineName);
  1297. }
  1298. *pPrinterHandle = CreatePrinterHandle( pFullPrinterName,
  1299. string,
  1300. pIniPrinter,
  1301. pIniPort,
  1302. pIniNetPort,
  1303. pIniJob,
  1304. TypeofHandle,
  1305. hPort,
  1306. pDefaults,
  1307. pIniSpooler,
  1308. DesiredAccess,
  1309. pSplClientInfo,
  1310. dwLevel,
  1311. hReadFile );
  1312. if( *pPrinterHandle ){
  1313. //
  1314. // Update the OpenPortError.
  1315. //
  1316. ((PSPOOL)*pPrinterHandle)->OpenPortError = OpenPortError;
  1317. } else {
  1318. SPLASSERT( GetLastError( ));
  1319. RouterReturnValue = ROUTER_STOP_ROUTING;
  1320. }
  1321. }
  1322. WrapUp:
  1323. LeaveSplSem();
  1324. //
  1325. // Don't have an SplOutSem as we could be called recursively.
  1326. //
  1327. switch( RouterReturnValue ){
  1328. case ROUTER_SUCCESS:
  1329. DBGMSG( DBG_TRACE, ("OpenPrinter returned handle %x\n", *pPrinterHandle));
  1330. SPLASSERT( *pPrinterHandle );
  1331. break;
  1332. case ROUTER_UNKNOWN:
  1333. SPLASSERT( !TypeofHandle && !pIniPrinter && !pIniPort &&
  1334. !pIniNetPort && !pIniJob && !hPort );
  1335. //
  1336. // hPort should not be valid. If it is, we have leaked a handle.
  1337. //
  1338. SPLASSERT( !hPort );
  1339. SPLASSERT( hReadFile == INVALID_HANDLE_VALUE );
  1340. DBGMSG( DBG_TRACE, ( "OpenPrinter failed, invalid name "TSTR"\n",
  1341. pFullPrinterName ));
  1342. SetLastError( ERROR_INVALID_NAME );
  1343. break;
  1344. case ROUTER_STOP_ROUTING:
  1345. LastError = GetLastError();
  1346. SPLASSERT( LastError );
  1347. //
  1348. // On failure, we may have opened a port or file handle. We need
  1349. // to close it since we won't return a valid handle, and
  1350. // so ClosePrinter will never get called.
  1351. //
  1352. if( hPort != INVALID_PORT_HANDLE ) {
  1353. ClosePrinter( hPort );
  1354. }
  1355. if ( pIniJob && (pIniJob->hFileItem == INVALID_HANDLE_VALUE) )
  1356. {
  1357. if ( hReadFile != INVALID_HANDLE_VALUE ) {
  1358. CloseHandle( hReadFile );
  1359. hReadFile = INVALID_HANDLE_VALUE;
  1360. }
  1361. }
  1362. DBGMSG( DBG_TRACE, ("OpenPrinter "TSTR" failed: Error %d\n",
  1363. pFullPrinterName, GetLastError()));
  1364. SetLastError( LastError );
  1365. break;
  1366. }
  1367. return RouterReturnValue;
  1368. }
  1369. BOOL
  1370. SplClosePrinter(
  1371. HANDLE hPrinter
  1372. )
  1373. {
  1374. PSPOOL pSpool=(PSPOOL)hPrinter;
  1375. PSPOOL *ppIniSpool = NULL;
  1376. PINISPOOLER pIniSpoolerDecRef = NULL;
  1377. PSPLMAPVIEW pSplMapView;
  1378. PMAPPED_JOB pMappedJob;
  1379. BOOL bValid;
  1380. DWORD Position;
  1381. //
  1382. // Allow us to close zombied handles.
  1383. //
  1384. EnterSplSem();
  1385. pSpool->Status &= ~SPOOL_STATUS_ZOMBIE;
  1386. if (pSpool->TypeofHandle & PRINTER_HANDLE_XCV_PORT) {
  1387. bValid = ValidateXcvHandle(pSpool->pIniXcv);
  1388. } else {
  1389. bValid = ValidateSpoolHandle(pSpool, 0);
  1390. }
  1391. LeaveSplSem();
  1392. if( !bValid ){
  1393. return FALSE;
  1394. }
  1395. if (!(pSpool->TypeofHandle & PRINTER_HANDLE_JOB) &&
  1396. pSpool->pIniJob &&
  1397. (pSpool->Status & SPOOL_STATUS_ADDJOB)) {
  1398. LocalScheduleJob(hPrinter, pSpool->pIniJob->JobId);
  1399. }
  1400. if (pSpool->Status & SPOOL_STATUS_STARTDOC) {
  1401. // it looks as though this might cause a double
  1402. // decrement of pIniJob->cRef once inside LocalEndDocPrinter
  1403. // and the other later in this routine.
  1404. LocalEndDocPrinter(hPrinter);
  1405. }
  1406. if (pSpool->TypeofHandle & PRINTER_HANDLE_JOB) {
  1407. if (pSpool->TypeofHandle & PRINTER_HANDLE_DIRECT) {
  1408. //
  1409. // If EndDoc is still waiting for a final ReadPrinter
  1410. //
  1411. if (pSpool->pIniJob->cbBuffer) { // Amount last transmitted
  1412. //
  1413. // Wake up the EndDoc Thread
  1414. //
  1415. SetEvent(pSpool->pIniJob->WaitForRead);
  1416. SplOutSem();
  1417. //
  1418. // Wait until he is finished
  1419. //
  1420. WaitForSingleObject(pSpool->pIniJob->WaitForWrite, INFINITE);
  1421. EnterSplSem();
  1422. //
  1423. // Now it is ok to close the handles
  1424. //
  1425. if (!CloseHandle(pSpool->pIniJob->WaitForWrite)) {
  1426. DBGMSG(DBG_WARNING, ("CloseHandle failed %d %d\n",
  1427. pSpool->pIniJob->WaitForWrite, GetLastError()));
  1428. }
  1429. if (!CloseHandle(pSpool->pIniJob->WaitForRead)) {
  1430. DBGMSG(DBG_WARNING, ("CloseHandle failed %d %d\n",
  1431. pSpool->pIniJob->WaitForRead, GetLastError()));
  1432. }
  1433. pSpool->pIniJob->WaitForRead = NULL;
  1434. pSpool->pIniJob->WaitForWrite = NULL;
  1435. LeaveSplSem();
  1436. }
  1437. DBGMSG(DBG_TRACE, ("ClosePrinter(DIRECT):cRef = %d\n", pSpool->pIniJob->cRef));
  1438. }
  1439. EnterSplSem();
  1440. DBGMSG(DBG_TRACE, ("ClosePrinter:cRef = %d\n", pSpool->pIniJob->cRef));
  1441. DECJOBREF(pSpool->pIniJob);
  1442. DeleteJobCheck(pSpool->pIniJob);
  1443. LeaveSplSem();
  1444. }
  1445. // Unmap all views of the spool file and close file mapping handles
  1446. while (pSplMapView = pSpool->pSplMapView) {
  1447. pSpool->pSplMapView = pSplMapView->pNext;
  1448. if (pSplMapView->pStartMapView) {
  1449. UnmapViewOfFile( (LPVOID) pSplMapView->pStartMapView);
  1450. }
  1451. // CreateFileMapping returns NULL (not INVALID_HANDLE_VALUE) for failure
  1452. if (pSplMapView->hMapSpoolFile) {
  1453. CloseHandle(pSplMapView->hMapSpoolFile);
  1454. }
  1455. FreeSplMem(pSplMapView);
  1456. }
  1457. // Delete all mapped spool files that are not required
  1458. EnterSplSem();
  1459. while (pMappedJob = pSpool->pMappedJob)
  1460. {
  1461. pSpool->pMappedJob = pMappedJob->pNext;
  1462. if (!pSpool->pIniPrinter ||
  1463. !FindJob(pSpool->pIniPrinter, pMappedJob->JobId, &Position))
  1464. {
  1465. // The job is gone and we have to delete the spool file
  1466. LeaveSplSem();
  1467. //
  1468. // This may need looking at for File Pooling
  1469. //
  1470. DeleteFile(pMappedJob->pszSpoolFile);
  1471. EnterSplSem();
  1472. if (pSpool->pIniPrinter)
  1473. {
  1474. vMarkOff( pSpool->pIniPrinter->pIniSpooler->hJobIdMap,
  1475. pMappedJob->JobId );
  1476. }
  1477. }
  1478. FreeSplMem(pMappedJob->pszSpoolFile);
  1479. FreeSplMem(pMappedJob);
  1480. }
  1481. LeaveSplSem();
  1482. if ( pSpool->hReadFile != INVALID_HANDLE_VALUE ) {
  1483. // Move the file pointer to the number of bytes committed and set the end of
  1484. // file.
  1485. if ((pSpool->pIniJob->Status & JOB_TYPE_OPTIMIZE) &&
  1486. SetFilePointer(pSpool->hReadFile, pSpool->pIniJob->dwValidSize,
  1487. NULL, FILE_BEGIN) != 0xffffffff) {
  1488. SetEndOfFile(pSpool->hReadFile);
  1489. }
  1490. //
  1491. // File pooling Change, we close the file handle if we aren't file
  1492. // pooling and we reset the seek pointer if we are file pooling.
  1493. //
  1494. if (pSpool->pIniJob)
  1495. {
  1496. if (pSpool->pIniJob->hFileItem == INVALID_HANDLE_VALUE)
  1497. {
  1498. if ( !CloseHandle( pSpool->hReadFile ) ) {
  1499. DBGMSG(DBG_WARNING, ("ClosePrinter CloseHandle(%d) failed %d\n", pSpool->hReadFile, GetLastError()));
  1500. }
  1501. }
  1502. else
  1503. {
  1504. //
  1505. // People call ClosePrinter / OpenPrinter in sequence to be able
  1506. // to read from the beginning of the spool file again. To get the
  1507. // same effect, we need to set the seek pointer back to the
  1508. // beginning of the hReadFile.
  1509. //
  1510. DWORD rc = ERROR_SUCCESS;
  1511. rc = SetFilePointer(pSpool->hReadFile, 0, NULL, FILE_BEGIN);
  1512. if (rc != ERROR_SUCCESS)
  1513. {
  1514. DBGMSG(DBG_WARNING, ("ClosePrinter SetFilePointer(%p) failed %d\n", pSpool->hReadFile, rc));
  1515. }
  1516. }
  1517. }
  1518. }
  1519. //
  1520. // Close the handle that was opened via OpenPrinterPort:
  1521. //
  1522. if (pSpool->hPort) {
  1523. if (pSpool->hPort != INVALID_PORT_HANDLE) {
  1524. ClosePrinter(pSpool->hPort);
  1525. } else {
  1526. DBGMSG(DBG_WARNING, ("ClosePrinter ignoring bad port handle.\n"));
  1527. }
  1528. }
  1529. EnterSplSem();
  1530. //
  1531. // Remove us from the linked list of handles:
  1532. //
  1533. if (pSpool->TypeofHandle & PRINTER_HANDLE_PRINTER) {
  1534. SPLASSERT( pSpool->pIniPrinter->signature == IP_SIGNATURE );
  1535. ppIniSpool = &pSpool->pIniPrinter->pSpool;
  1536. }
  1537. else if ((pSpool->TypeofHandle & PRINTER_HANDLE_SERVER) ||
  1538. (pSpool->TypeofHandle & PRINTER_HANDLE_XCV_PORT)) {
  1539. SPLASSERT( pSpool->pIniSpooler->signature == ISP_SIGNATURE );
  1540. if (pSpool->TypeofHandle & PRINTER_HANDLE_XCV_PORT)
  1541. XcvClose(pSpool->pIniXcv);
  1542. pIniSpoolerDecRef = pSpool->pIniSpooler;
  1543. ppIniSpool = &pSpool->pIniSpooler->pSpool;
  1544. }
  1545. if (ppIniSpool) {
  1546. while (*ppIniSpool && *ppIniSpool != pSpool)
  1547. ppIniSpool = &(*ppIniSpool)->pNext;
  1548. if (*ppIniSpool)
  1549. *ppIniSpool = pSpool->pNext;
  1550. else {
  1551. DBGMSG( DBG_WARNING, ( "Didn't find pSpool %08x in linked list\n", pSpool ) );
  1552. }
  1553. }
  1554. if (pSpool->pIniPrinter) {
  1555. DECPRINTERREF( pSpool->pIniPrinter );
  1556. DeletePrinterCheck(pSpool->pIniPrinter);
  1557. }
  1558. DeletePrinterHandle(pSpool);
  1559. if( pIniSpoolerDecRef ){
  1560. DECSPOOLERREF( pIniSpoolerDecRef );
  1561. }
  1562. LeaveSplSem();
  1563. //
  1564. // Don't call SplOutSem() since SplAddPrinter calls
  1565. // use from inside the critical section.
  1566. //
  1567. return TRUE;
  1568. }
  1569. #ifdef _HYDRA_
  1570. ULONG
  1571. GetClientSessionId(
  1572. )
  1573. /*++
  1574. Routine Description:
  1575. Get the SessionID from the client who we should be impersonating. If any
  1576. errors, we return 0 to mean the Console SessionId since this may be a
  1577. remote network call.
  1578. Arguments:
  1579. Return Value:
  1580. SessionID of the effective token
  1581. --*/
  1582. {
  1583. BOOL Result;
  1584. HANDLE TokenHandle;
  1585. ULONG SessionId, ReturnLength;
  1586. //
  1587. // We should be impersonating the client, so we will get the
  1588. // SessionId from out token.
  1589. //
  1590. // We may not have a valid one if this is a remote network
  1591. // connection.
  1592. Result = OpenThreadToken(
  1593. GetCurrentThread(),
  1594. TOKEN_QUERY,
  1595. FALSE, // Use impersonation
  1596. &TokenHandle
  1597. );
  1598. if( Result ) {
  1599. //
  1600. // Query the SessionID from the token added by HYDRA
  1601. //
  1602. Result = GetTokenInformation(
  1603. TokenHandle,
  1604. (TOKEN_INFORMATION_CLASS)TokenSessionId,
  1605. &SessionId,
  1606. sizeof(SessionId),
  1607. &ReturnLength
  1608. );
  1609. if( !Result ) {
  1610. SessionId = 0; // Default to console
  1611. }
  1612. CloseHandle( TokenHandle );
  1613. }
  1614. else {
  1615. SessionId = 0;
  1616. }
  1617. return( SessionId );
  1618. }
  1619. #endif