/*++ Copyright (c) 1990 - 1996 Microsoft Corporation Module Name: openprn.c Abstract: This module provides all the public exported APIs relating to Printer management for the Local Print Providor LocalOpenPrinter SplClosePrinter Author: Dave Snipp (DaveSn) 15-Mar-1991 Revision History: Matthew A Felton (mattfe) June 1994 RapidPrint Jan 95 Cleanup CreatePrinterHandle --*/ #define NOMINMAX #include #include "jobid.h" #include "filepool.hxx" #define SZXCVPORT L"XcvPort " #define SZXCVMONITOR L"XcvMonitor " LPCTSTR pszLocalOnlyToken = L"LocalOnly"; LPCTSTR pszLocalsplOnlyToken = L"LocalsplOnly"; HANDLE CreatePrinterHandle( LPWSTR pPrinterName, LPWSTR pFullMachineName, PINIPRINTER pIniPrinter, PINIPORT pIniPort, PINIPORT pIniNetPort, PINIJOB pIniJob, DWORD TypeofHandle, HANDLE hPort, PPRINTER_DEFAULTS pDefaults, PINISPOOLER pIniSpooler, DWORD DesiredAccess, LPBYTE pSplClientInfo, DWORD dwLevel, HANDLE hReadFile ) { PSPOOL pSpool = NULL; BOOL bStatus = FALSE; HANDLE hReturnHandle = NULL; LPDEVMODE pDevMode = NULL; PSPLCLIENT_INFO_1 pSplClientInfo1 = (PSPLCLIENT_INFO_1)pSplClientInfo; DWORD ObjectType; SPLASSERT( pIniSpooler->signature == ISP_SIGNATURE ); if ( dwLevel && ( dwLevel != 1 || !pSplClientInfo) ) { DBGMSG(DBG_ERROR, ("CreatePrintHandle: Invalid client info %x - %d\n", pSplClientInfo, dwLevel)); pSplClientInfo = NULL; } try { pSpool = (PSPOOL)AllocSplMem( SPOOL_SIZE ); if ( pSpool == NULL ) { DBGMSG( DBG_WARNING, ("CreatePrinterHandle failed to allocate SPOOL %d\n", GetLastError() )); leave; } pSpool->signature = SJ_SIGNATURE; pSpool->pIniPrinter = pIniPrinter; pSpool->hReadFile = hReadFile; pSpool->pIniPort = pIniPort; pSpool->pIniNetPort = pIniNetPort; pSpool->pIniJob = pIniJob; pSpool->TypeofHandle = TypeofHandle; pSpool->hPort = hPort; pSpool->Status = 0; pSpool->pDevMode = NULL; pSpool->pName = AllocSplStr( pPrinterName ); pSpool->pFullMachineName = AllocSplStr( pFullMachineName ); pSpool->pSplMapView = NULL; pSpool->pMappedJob = NULL; if ( pSpool->pName == NULL || ( pFullMachineName && !pSpool->pFullMachineName )) { leave; } pSpool->pIniSpooler = pIniSpooler; #ifdef _HYDRA_ pSpool->SessionId = GetClientSessionId(); #endif // // Check if it's a local call. // if( TypeofHandle & PRINTER_HANDLE_REMOTE_CALL ) { // // We get other useful info like build #, client architecture // we do not need this info now -- so we do not put it in PSPOOL // if ( !pSplClientInfo ) { if ( IsNamedPipeRpcCall() ) TypeofHandle |= PRINTER_HANDLE_3XCLIENT; } else if ( dwLevel == 1 ) { SPLASSERT(pSplClientInfo1->pUserName && pSplClientInfo1->pMachineName); CopyMemory(&pSpool->SplClientInfo1, pSplClientInfo1, sizeof(SPLCLIENT_INFO_1)); pSpool->SplClientInfo1.pUserName = AllocSplStr(pSplClientInfo1->pUserName); pSpool->SplClientInfo1.pMachineName = AllocSplStr(pSplClientInfo1->pMachineName); if ( !pSpool->SplClientInfo1.pUserName || !pSpool->SplClientInfo1.pMachineName ) { DBGMSG(DBG_WARNING, ("CreatePrinterHandle: could not allocate memory for user name or machine name\n")); } } } if ((TypeofHandle & PRINTER_HANDLE_SERVER) || (TypeofHandle & PRINTER_HANDLE_XCV_PORT)) { bStatus = ValidateObjectAccess( SPOOLER_OBJECT_SERVER, DesiredAccess, pSpool, &pSpool->GrantedAccess, pIniSpooler ); if ( bStatus && (TypeofHandle & PRINTER_HANDLE_REMOTE_CALL) && ( (DesiredAccess & SERVER_ACCESS_ADMINISTER) && ValidateObjectAccess(SPOOLER_OBJECT_SERVER, SERVER_ACCESS_ADMINISTER, NULL, NULL, pIniSpooler)) ){ pSpool->TypeofHandle |= PRINTER_HANDLE_REMOTE_ADMIN; } ObjectType = SPOOLER_OBJECT_SERVER; } else if( TypeofHandle & PRINTER_HANDLE_JOB ){ bStatus = ValidateObjectAccess( SPOOLER_OBJECT_DOCUMENT, DesiredAccess, pSpool->pIniJob, &pSpool->GrantedAccess, pIniSpooler ); ObjectType = SPOOLER_OBJECT_DOCUMENT; } else { bStatus = ValidateObjectAccess( SPOOLER_OBJECT_PRINTER, DesiredAccess, pSpool, &pSpool->GrantedAccess, pIniSpooler ); ObjectType = SPOOLER_OBJECT_PRINTER; } MapGenericToSpecificAccess( ObjectType, pSpool->GrantedAccess, &pSpool->GrantedAccess); if ( !bStatus ) { SetLastError(ERROR_ACCESS_DENIED); leave; } if ( pIniPrinter ) { if ( pDefaults ) { // // Allocate DevMode // if ( pDefaults->pDevMode ) { pDevMode = pDefaults->pDevMode; } else { pDevMode = pIniPrinter->pDevMode; } if ( pDevMode != NULL ) { pSpool->pDevMode = AllocSplMem( pDevMode->dmSize + pDevMode->dmDriverExtra ); if ( pSpool->pDevMode == NULL ) { DBGMSG(DBG_WARNING, ("CreatePrinterHandle failed allocation for devmode %d\n", GetLastError() )); leave; } memcpy( pSpool->pDevMode, pDevMode, pDevMode->dmSize + pDevMode->dmDriverExtra ); } } // // Allocate Datype and Print Processor // if ( pDefaults && pDefaults->pDatatype ) { pSpool->pDatatype = AllocSplStr( pDefaults->pDatatype ); pSpool->pIniPrintProc = FindDatatype( pIniPrinter->pIniPrintProc, pSpool->pDatatype ); } else { pSpool->pDatatype = AllocSplStr( pIniPrinter->pDatatype ); pSpool->pIniPrintProc = pIniPrinter->pIniPrintProc; } if ( pSpool->pIniPrintProc == NULL ) { DBGMSG( DBG_WARNING,("CreatePrinterHandle failed to PrintProcessor for datatype %ws %d\n", pSpool->pDatatype, GetLastError() )); SetLastError( ERROR_INVALID_DATATYPE ); leave; } SPLASSERT( pSpool->pIniPrintProc->signature == IPP_SIGNATURE ); pSpool->pIniPrintProc->cRef++; if ( pSpool->pDatatype == NULL ) { DBGMSG( DBG_WARNING,("CreatePrinterHandle failed to allocate DataType %x\n", GetLastError() )); SetLastError( ERROR_INVALID_DATATYPE ); leave; } } // Add us to the linked list of handles for this printer. // This will be scanned when a change occurs on the printer, // and will be updated with a flag indicating what type of // change it was. // There is a flag for each handle, because we cannot guarantee // that all threads will have time to reference a flag in the // INIPRINTER before it is updated. if ( TypeofHandle & PRINTER_HANDLE_PRINTER ) { pSpool->pNext = pSpool->pIniPrinter->pSpool; pSpool->pIniPrinter->pSpool = pSpool; } else if ( (TypeofHandle & PRINTER_HANDLE_SERVER) || (TypeofHandle & PRINTER_HANDLE_XCV_PORT) ) { // // For server handles, hang them off the global IniSpooler: // pSpool->pNext = pIniSpooler->pSpool; pIniSpooler->pSpool = pSpool; INCSPOOLERREF( pIniSpooler ); } else if( TypeofHandle & PRINTER_HANDLE_JOB ){ INCJOBREF( pIniJob ); } // Note Only PRINTER_HANDLE_PRINTER are attatched to the // pIniPrinter, since those are the handle which will require // change notifications. if ( pSpool->pIniPrinter != NULL ) { INCPRINTERREF( pSpool->pIniPrinter ); } hReturnHandle = (HANDLE)pSpool; } finally { if ( hReturnHandle == NULL ) { // Failure CleanUP if ( pSpool != NULL ) { FreeSplStr(pSpool->SplClientInfo1.pUserName); FreeSplStr(pSpool->SplClientInfo1.pMachineName); FreeSplStr( pSpool->pName ) ; FreeSplStr( pSpool->pDatatype ); FreeSplStr(pSpool->pFullMachineName); if ( pSpool->pIniPrintProc != NULL ) pSpool->pIniPrintProc->cRef--; if ( pSpool->pDevMode ) FreeSplMem( pSpool->pDevMode ); FreeSplMem( pSpool ); pSpool = NULL; } } } return hReturnHandle; } BOOL DeletePrinterHandle( PSPOOL pSpool ) { BOOL bRet = FALSE; SplInSem(); if (pSpool->pIniPrintProc) { pSpool->pIniPrintProc->cRef--; } if (pSpool->pDevMode) FreeSplMem(pSpool->pDevMode); FreeSplStr(pSpool->SplClientInfo1.pUserName); FreeSplStr(pSpool->SplClientInfo1.pMachineName); FreeSplStr(pSpool->pDatatype); SetSpoolClosingChange(pSpool); FreeSplStr(pSpool->pName); FreeSplStr(pSpool->pFullMachineName); bRet = ObjectCloseAuditAlarm( szSpooler, pSpool, pSpool->GenerateOnClose ); // // If there is a WaitForPrinterChange outstanding, we can't free // the pSpool, since we may try and reference it. // // Log warning for freed printer handle DBGMSG(DBG_TRACE, ("DeletePrinterHandle 0x%x", pSpool)); if (pSpool->ChangeEvent) { pSpool->eStatus |= STATUS_PENDING_DELETION; } else { FreeSplMem(pSpool); } return TRUE; } DWORD CreateServerHandle( LPWSTR pPrinterName, LPHANDLE pPrinterHandle, LPPRINTER_DEFAULTS pDefaults, PINISPOOLER pIniSpooler, DWORD dwTypeofHandle ) { DWORD DesiredAccess; DWORD ReturnValue = ROUTER_STOP_ROUTING; DBGMSG(DBG_TRACE, ("OpenPrinter(%ws)\n", pPrinterName ? pPrinterName : L"NULL")); EnterSplSem(); if (!pDefaults || !pDefaults->DesiredAccess) DesiredAccess = SERVER_READ; else DesiredAccess = pDefaults->DesiredAccess; if (*pPrinterHandle = CreatePrinterHandle( pIniSpooler->pMachineName, pPrinterName, NULL, NULL, NULL, NULL, dwTypeofHandle, NULL, pDefaults, pIniSpooler, DesiredAccess, NULL, 0, INVALID_HANDLE_VALUE )){ ReturnValue = ROUTER_SUCCESS; } LeaveSplSem(); DBGMSG(DBG_TRACE, ("OpenPrinter returned handle %08x\n", *pPrinterHandle)); return ReturnValue; } PINIPRINTER FindPrinterShare( LPCWSTR pszShareName, PINISPOOLER pIniSpooler ) /*++ Routine Description: Try and find the share name in our list of printers. Note: Even if the printer isn't shared, we still return a match. The caching code will work because it explicitly turns off the PRINTER_ATTRIBUTE_SHARE bit so that the cache pIniSpooler doesn't create a server thread or call NetShareAdd/Del. In the future, consider changing this to check the share bit. Create a new bit SPL_SHARE_PRINTERS that indicates whether sharing housekeeping should be done. Arguments: pszShareName - Name of share to search for. Return Value: PINIPRINTER Printer that has the share name, NULL if no printer. --*/ { PINIPRINTER pIniPrinter; SplInSem(); if (pszShareName && pszShareName[0]) { for( pIniPrinter = pIniSpooler->pIniPrinter; pIniPrinter; pIniPrinter = pIniPrinter->pNext ){ if (pIniPrinter->pShareName && !lstrcmpi(pIniPrinter->pShareName, pszShareName)) { return pIniPrinter; } } } return NULL; } PINISPOOLER LocalFindSpoolerByNameIncRef( LPWSTR pszPrinterName, LPTSTR *ppszLocalName OPTIONAL ) /*++ Routine Description: See if the printer is owned by localspl. This is a special case to check if it's a masquarading printer. Normally we would check for \\Server\Printer to see if \\Server is our machine, but we have to look for \\MasqServer\Printer too. Arguments: pPrinterName - Name to check. ppszLocalName - Returns pointer to local name. OPTIONAL Return Value: PINISPOOLER - Spooler match. NULL - No match. --*/ { PINISPOOLER pIniSpooler; LPWSTR pTemp; if (!ppszLocalName) ppszLocalName = &pTemp; EnterSplSem(); pIniSpooler = FindSpoolerByName( pszPrinterName, ppszLocalName ); if( !pIniSpooler ){ // // Check if it's a masq printer. // // If the local name isn't the same as the original name, it // is in the syntax "\\server\printer." In this case it may // be a masq printer, so check if the printer exists in the // local spooler by this name. // if( *ppszLocalName != pszPrinterName ){ // // Search for the printer, but remove any suffixes it // may have. // WCHAR string[MAX_UNC_PRINTER_NAME + PRINTER_NAME_SUFFIX_MAX]; wcscpy(string, pszPrinterName); if( pTemp = wcschr( string, L',' )){ *pTemp = 0; } if( FindPrinter( string, pLocalIniSpooler )){ // // The masq printer exists. The local name for this // masq printer is "\\MasqServer\Printer," so we must // reflect this change in ppszLocalName. This will ensure // that the pIniPrinter is found. // *ppszLocalName = pszPrinterName; pIniSpooler = pLocalIniSpooler; } } } if( pIniSpooler ){ INCSPOOLERREF( pIniSpooler ); } LeaveSplSem(); return pIniSpooler; } VOID LocalFindSpoolerByNameDecRef( PINISPOOLER pIniSpooler ) /*++ Routine Description: Matching call to LocalFindSpoolerByNameIncRef. Arguments: pIniSpooler - Spooler to derement; can be NULL. Return Value: --*/ { EnterSplSem(); if( pIniSpooler ){ DECSPOOLERREF( pIniSpooler ); } LeaveSplSem(); } DWORD LocalOpenPrinter( LPWSTR pPrinterName, LPHANDLE pPrinterHandle, LPPRINTER_DEFAULTS pDefaults ) { return LocalOpenPrinterEx( pPrinterName, pPrinterHandle, pDefaults, NULL, 0 ); } DWORD LocalOpenPrinterEx( LPWSTR pPrinterName, LPHANDLE pPrinterHandle, LPPRINTER_DEFAULTS pDefaults, LPBYTE pSplClientInfo, DWORD dwLevel ) { DWORD dwReturn; LPWSTR pszLocalName; PINISPOOLER pIniSpooler = LocalFindSpoolerByNameIncRef( pPrinterName, &pszLocalName); // // WMI Trace Event. // LogWmiTraceEvent(0, EVENT_TRACE_TYPE_SPL_ENDTRACKTHREAD, NULL); if( !pIniSpooler ) { // // Check for PrinterName,LocalsplOnly. // If we see this token, then fail the call since // we only want to check localspl. // LPCTSTR pSecondPart; if( pSecondPart = wcschr( pPrinterName, L',' )){ ++pSecondPart; if( wcscmp( pSecondPart, pszLocalsplOnlyToken ) == STRINGS_ARE_EQUAL ){ SetLastError( ERROR_INVALID_PRINTER_NAME ); return ROUTER_STOP_ROUTING; } } SetLastError( ERROR_INVALID_NAME ); return ROUTER_UNKNOWN; } dwReturn = SplOpenPrinter( pPrinterName, pPrinterHandle, pDefaults, pIniSpooler, pSplClientInfo, dwLevel); LocalFindSpoolerByNameDecRef( pIniSpooler ); // // We need to give other provider a chance to get the printer name // #if 0 // // If we didn't find a printer but we did find a pIniSpooler and // the printer name was a remote name (e.g., "\\localmachine\printer"), // then we should stop routing. In this case, the local machine owns // the pIniSpooler, but it didn't find any printers. Don't bother // checking the other providers. // // In theory, we should be able to quit for local printers too // (e.g., OpenPrinter( "UnknownPrinter", ... )), but if any other // provider expects to get this name, they no longer will. // if( dwReturn == ROUTER_UNKNOWN && pszLocalName != pPrinterName ){ dwReturn = ROUTER_STOP_ROUTING; } #endif return dwReturn; } DWORD OpenLocalPrinterName( LPCWSTR pPrinterName, PINISPOOLER pIniSpooler, PDWORD pTypeofHandle, PINIPRINTER* ppIniPrinter, PINIPORT* ppIniPort, PINIPORT* ppIniNetPort, PHANDLE phPort, PDWORD pOpenPortError, LPPRINTER_DEFAULTS pDefaults ) { PINIPRINTER pIniPrinter; PINIPORT pIniPort; PINIPORT pIniNetPort = NULL; BOOL bOpenPrinterPort; LPWSTR pDatatype; // // If the printer name is the name of a local printer: // // Find the first port the printer's attached to. // // If the port has a monitor (e.g. LPT1:, COM1 etc.), // we're OK, // Otherwise // try to open the port - this may be a network printer // if( ( pIniPrinter = FindPrinter( pPrinterName, pIniSpooler )) || ( pIniPrinter = FindPrinterShare( pPrinterName, pIniSpooler ))) { pIniPort = FindIniPortFromIniPrinter( pIniPrinter ); if( pIniPort && ( pIniPort->Status & PP_MONITOR )){ // // A Printer that has a Port with a Monitor is not a // DownLevel Connection (or LocalPrinter acting as a // remote printer - "Masquarade" case). // pIniPort = NULL; } pDatatype = (pDefaults && pDefaults->pDatatype) ? pDefaults->pDatatype : NULL; // // Validate datatypes for both masq and local. // if( pDatatype && !FindDatatype( NULL, pDatatype )){ goto BadDatatype; } if( pIniPort ){ // // DownLevel Connection Printer; save it in pIniNetPort. // SetPrinterPorts checks this value. // pIniNetPort = pIniPort; // // Validate datatype. We only send RAW across the net // to masq printers. // if( pDatatype && !ValidRawDatatype( pDatatype )){ goto BadDatatype; } // // There is a network port associated with this printer. // Make sure we can open it, and get the handle to use on // future API calls: // INCPRINTERREF(pIniPrinter); LeaveSplSem(); bOpenPrinterPort = OpenPrinterPortW( pIniPort->pName, phPort, pDefaults ); EnterSplSem(); DECPRINTERREF(pIniPrinter); if( !bOpenPrinterPort ){ *phPort = INVALID_PORT_HANDLE; *pOpenPortError = GetLastError(); // // Must be non-zero otherwise it looks like success. // SPLASSERT( *pOpenPortError ); if( *pOpenPortError == ERROR_INVALID_PASSWORD ) { // // This call should fail if it's because the password // is invalid, then winspool or printman can prompt // for the password. // DBGMSG(DBG_WARNING, ("OpenPrinterPort1( %ws ) failed with ERROR_INVALID_PASSWORD . OpenPrinter returning FALSE\n", pIniPort->pName )); return ROUTER_STOP_ROUTING; } DBGMSG(DBG_WARNING, ("OpenPrinterPort1( %ws ) failed: Error %d. OpenPrinter returning TRUE\n", pIniPort->pName, *pOpenPortError)); } else { // // Clear the placeholder bit from the pIniPort status. This // belongs to a partial print provider. // pIniPort->Status &= ~PP_PLACEHOLDER; } } else { // // Not a masq case. If it's direct, it must be raw. // // Note: we will use the default if no datatype is specified. // However, if the default datatype is non-RAW and the // printer is direct, the open will succeed using a // non-RAW datatype! // if(( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_DIRECT ) && pDatatype && !ValidRawDatatype( pDatatype )) { goto BadDatatype; } } // // If this is a placeholder port, assume that it is a monitor port for now. // if (pIniPort && pIniPort->Status & PP_PLACEHOLDER) { pIniPort = NULL; pIniNetPort = NULL; } *pTypeofHandle |= ( pIniPort ? PRINTER_HANDLE_PORT : PRINTER_HANDLE_PRINTER ); *ppIniPort = pIniPort; *ppIniNetPort = pIniNetPort; *ppIniPrinter = pIniPrinter; return ROUTER_SUCCESS; } SetLastError( ERROR_INVALID_NAME ); return ROUTER_UNKNOWN; BadDatatype: SetLastError( ERROR_INVALID_DATATYPE ); return ROUTER_STOP_ROUTING; } DWORD CheckPrinterTokens( LPCWSTR string, LPCWSTR pSecondPart, PDWORD pTypeofHandle, PINISPOOLER pIniSpooler, PINIPRINTER *ppIniPrinter, PINIPORT *ppIniPort, PINIPORT *ppIniNetPort, PHANDLE phPort, PDWORD pOpenPortError, PPRINTER_DEFAULTS pDefaults ) { typedef enum { kNone = 0, kLocalOnly = 1, kLocalsplOnly = 2 } ETOKEN_TYPE; ETOKEN_TYPE eTokenType = kNone; DWORD RouterReturnValue = ROUTER_UNKNOWN; // // LocalOnly // // Do not call OpenPrinterPort--use local settings only. // If not recognized by localspl, stop routing. // // This is used during upgrade of a downlevel printer connection. // The remote server may not be up, but we can return a print // handle to the local printer. Get/SetPrinterData calls will // succeed (for upgrade purposes), but printing will fail. // // LocalsplOnly // // Call OpenPrinterPort if necessary. // Stop routing after localspl even if not found. // // This is used when the system knows that the printer must exist // on the local machine, and does not want to route further. // This fixes the clustering problem when the server has a stale // print share and successfully validates it against win32spl since // it is cached. // if( wcsncmp( pSecondPart, pszLocalOnlyToken, wcslen(pszLocalOnlyToken) ) == STRINGS_ARE_EQUAL ){ eTokenType = kLocalOnly; } else if( wcsncmp( pSecondPart, pszLocalsplOnlyToken, wcslen(pszLocalsplOnlyToken) ) == STRINGS_ARE_EQUAL ){ eTokenType = kLocalsplOnly; } // // If we have a valid token, process it. // if( eTokenType != kNone ){ switch( eTokenType ){ case kLocalOnly: // // Find the printer associate with it. // *ppIniPrinter = FindPrinter( string, pIniSpooler ); if( *ppIniPrinter ){ *pTypeofHandle |= PRINTER_HANDLE_PRINTER; RouterReturnValue = ROUTER_SUCCESS; } else { RouterReturnValue = ROUTER_STOP_ROUTING; } break; case kLocalsplOnly: RouterReturnValue = OpenLocalPrinterName( string, pIniSpooler, pTypeofHandle, ppIniPrinter, ppIniPort, ppIniNetPort, phPort, pOpenPortError, pDefaults ); *pTypeofHandle = *pTypeofHandle & (~PRINTER_HANDLE_REMOTE_CALL); if( RouterReturnValue == ROUTER_UNKNOWN ){ RouterReturnValue = ROUTER_STOP_ROUTING; } } } DBGMSG( DBG_TRACE, ( "CheckPrinterTokens: %ws %d Requested %d %x\n", string, RouterReturnValue, *ppIniPrinter )); return RouterReturnValue; } DWORD CheckPrinterPortToken( LPCWSTR string, LPCWSTR pSecondPart, PDWORD pTypeofHandle, PINIPRINTER* ppIniPrinter, PINIPORT* ppIniPort, PINIJOB* ppIniJob, const LPPRINTER_DEFAULTS pDefaults, const PINISPOOLER pIniSpooler ) { if( wcsncmp( pSecondPart, L"Port", 4 ) != STRINGS_ARE_EQUAL || !( *ppIniPort = FindPort( string, pIniSpooler ))){ return ROUTER_UNKNOWN; } // // The name is the name of a port: // if( pDefaults && pDefaults->pDatatype && !ValidRawDatatype( pDefaults->pDatatype )) { SetLastError( ERROR_INVALID_DATATYPE ); return ROUTER_STOP_ROUTING; } if ( *ppIniJob = (*ppIniPort)->pIniJob ) { *ppIniPrinter = (*ppIniJob)->pIniPrinter; *pTypeofHandle |= PRINTER_HANDLE_PORT; } else if( (*ppIniPort)->cPrinters ){ // // There is no current job assigned to the port // So Open the First Printer Associated with // this port. // *ppIniPrinter = (*ppIniPort)->ppIniPrinter[0]; *pTypeofHandle |= PRINTER_HANDLE_PRINTER; } return ROUTER_SUCCESS; } DWORD CheckPrinterJobToken( LPCWSTR string, LPCWSTR pSecondPart, PDWORD pTypeofHandle, PINIPRINTER* ppIniPrinter, PINIJOB* ppIniJob, PHANDLE phReadFile, const PINISPOOLER pIniSpooler ) { HANDLE hImpersonationToken; DWORD Position, dwShareMode, dwDesiredAccess; DWORD JobId; PINIPRINTER pIniPrinter; PINIJOB pIniJob, pCurrentIniJob; PWSTR pszStr = NULL; if( wcsncmp( pSecondPart, L"Job ", 4 ) != STRINGS_ARE_EQUAL || !( pIniPrinter = FindPrinter( string, pIniSpooler ))){ return ROUTER_UNKNOWN; } // // Get the Job ID ",Job xxxx" // pSecondPart += 4; JobId = Myatol( (LPWSTR)pSecondPart ); pIniJob = FindJob( pIniPrinter, JobId, &Position ); if( pIniJob == NULL ) { DBGMSG( DBG_WARN, ("OpenPrinter failed to find Job %d\n", JobId )); return ROUTER_UNKNOWN; } DBGMSG( DBG_TRACE, ("OpenPrinter: pIniJob->cRef = %d\n", pIniJob->cRef)); if( pIniJob->Status & JOB_DIRECT ) { SplInSem(); *pTypeofHandle |= PRINTER_HANDLE_JOB | PRINTER_HANDLE_DIRECT; goto Success; } // // If this job is assigned to a port // Then pick up the correct chained jobid file instead of the master // JobId. // if ( pIniJob->pCurrentIniJob != NULL ) { SPLASSERT( pIniJob->pCurrentIniJob->signature == IJ_SIGNATURE ); DBGMSG( DBG_TRACE,("CheckPrinterJobToken pIniJob %x JobId %d using chain JobId %d\n", pIniJob, pIniJob->JobId, pIniJob->pCurrentIniJob->JobId )); pCurrentIniJob = pIniJob->pCurrentIniJob; SPLASSERT( pCurrentIniJob->signature == IJ_SIGNATURE ); } else { pCurrentIniJob = pIniJob; } if ( pCurrentIniJob->hFileItem != INVALID_HANDLE_VALUE ) { LeaveSplSem(); hImpersonationToken = RevertToPrinterSelf(); GetReaderFromHandle(pCurrentIniJob->hFileItem, phReadFile); ImpersonatePrinterClient( hImpersonationToken ); if ( *phReadFile && (*phReadFile != INVALID_HANDLE_VALUE)) { GetNameFromHandle(pCurrentIniJob->hFileItem, &pszStr, TRUE); wcscpy((LPWSTR)string, pszStr); FreeSplStr(pszStr); EnterSplSem(); *pTypeofHandle |= PRINTER_HANDLE_JOB; goto Success; } DBGMSG( DBG_WARN,("Filepools: Failed to get valid reader handle\n")); EnterSplSem(); } else { GetFullNameFromId( pCurrentIniJob->pIniPrinter, pCurrentIniJob->JobId, TRUE, (LPWSTR)string, FALSE ); // Bug 54845 // Even a user without previledge can open a ", JOB #" // if he is physically running on the machine. LeaveSplSem(); hImpersonationToken = RevertToPrinterSelf(); dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; if (pCurrentIniJob->Status & JOB_TYPE_OPTIMIZE) { dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; } else { dwDesiredAccess = GENERIC_READ; } *phReadFile = CreateFile(string, dwDesiredAccess, dwShareMode, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); ImpersonatePrinterClient( hImpersonationToken ); EnterSplSem(); if( *phReadFile != INVALID_HANDLE_VALUE ) { DBGMSG( DBG_TRACE, ( "OpenPrinter JobID %d pIniJob %x CreateFile( %ws ), hReadFile %x success", JobId, pIniJob, string, *phReadFile )); SplInSem(); *pTypeofHandle |= PRINTER_HANDLE_JOB; goto Success; } } DBGMSG( DBG_WARNING, ( "LocalOpenPrinter CreateFile(%ws) GENERIC_READ failed : %d\n", string, GetLastError())); SPLASSERT( GetLastError( )); return ROUTER_STOP_ROUTING; Success: *ppIniJob = pIniJob; *ppIniPrinter = pIniPrinter; return ROUTER_SUCCESS; } DWORD CheckXcvPortToken( LPCWSTR pszSecondPart, PDWORD pTypeofHandle, const LPPRINTER_DEFAULTS pDefaults, const PINISPOOLER pIniSpooler, PHANDLE phXcv ) { DWORD dwRet = ROUTER_SUCCESS; DWORD dwType; PCWSTR pszPort; DWORD dwTypeofHandle = *pTypeofHandle; if (!wcsncmp(pszSecondPart, SZXCVPORT, COUNTOF(SZXCVPORT) - 1)) { dwType = XCVPORT; dwTypeofHandle |= PRINTER_HANDLE_XCV_PORT; pszPort = (PCWSTR) pszSecondPart + COUNTOF(SZXCVPORT) - 1; } else if (!wcsncmp(pszSecondPart, SZXCVMONITOR, COUNTOF(SZXCVMONITOR) - 1)) { dwType = XCVMONITOR; dwTypeofHandle |= PRINTER_HANDLE_XCV_PORT; pszPort = (PCWSTR) pszSecondPart + COUNTOF(SZXCVMONITOR) - 1; } else dwRet = ROUTER_UNKNOWN; if (dwRet == ROUTER_SUCCESS) { dwRet = XcvOpen(NULL, pszPort, dwType, pDefaults, phXcv, pIniSpooler); if (dwRet == ROUTER_SUCCESS) *pTypeofHandle = dwTypeofHandle; } return dwRet; } DWORD SplOpenPrinter( LPWSTR pFullPrinterName, LPHANDLE pPrinterHandle, LPPRINTER_DEFAULTS pDefaults, PINISPOOLER pIniSpooler, LPBYTE pSplClientInfo, DWORD dwLevel ) /*++ Routine Description: OpenPrinter can open any of the following by specifying a string in pPrinterName:- Server \\MachineName NULL Job PrinterName, Job xxxx Port PortName, Port XcvPort \\MachineName\,XcvPort Port ,XcvPort Port XcvMonitor \\MachineName\,XcvMonitor Monitor ,XcvMonitor Monitor Printer PrinterName ShareName \\MachineName\PrinterName \\MachineName\ShareName PrinterName, LocalOnly ShareName, LocalOnly PrinterName, LocalsplOnly ShareName, LocalsplOnly Note for Printer there are two Types 1 - Regular LocalPrinter 2 - DownLevel Connection Printer For type 2 a LocalPrinter exists ( pIniPrinter ) but its port does not have a monitor associated with it. In this case we also open the port ( typically \\share\printer of a remote machine ) before we return success. GUI Applications usually use Server and Printer Type Job and Port are used by Print Processors:- A print processor will Open a Job then read the job using ReadPrinter. A print processor will output to a Port by opening the PortName, Port and using WritePrinter. Usually these strings "PrinterName, Job xxx" "PortName, Port" are passed to the print processor by the spooler and are currently not documented. We do know that some OEMs have figured out the extentions and we might break someone if we change them. Type LocalOnlyToken is used by a Printer Driver:- Used when we need to upgrade a printer's settings from an older version of the driver to a newer one (see drvupgrd.c for details). This was added in NT 3.51. Type LocasplOnlyToken is used by server:- Indicates that we should check localspl only (local or masq). Other providers will not be called. Arguments: pPrinterName - PrinterName ( see above for different types of PrinterName ) pPrinterHandle - Address to put hPrinter on Success pDefaults - Optional, allows user to specify Datatype, DevMode, DesiredAccess. pIniSpooler - This spooler "owns" the printer. We will only check against this spooler, and we assume that the callee has already checked that "\\server\printer" lives on this pIniSpooler (i.e., we are \\server). ( see SDK Online Help for full explanation ) Return Value: TRUE - *pPrinterHandle will have a PrinterHandle FALSE - use GetLastError --*/ { PINIPRINTER pIniPrinter = NULL; PINIPORT pIniPort = NULL; PINIPORT pIniNetPort = NULL; DWORD LastError = 0; LPWSTR pPrinterName = pFullPrinterName; WCHAR string[MAX_UNC_PRINTER_NAME + PRINTER_NAME_SUFFIX_MAX]; PINIJOB pIniJob = NULL; HANDLE hReadFile = INVALID_HANDLE_VALUE; DWORD TypeofHandle = 0; LPWSTR pSecondPart = NULL; HANDLE hPort = INVALID_PORT_HANDLE; DWORD OpenPortError = NO_ERROR; BOOL bRemoteUserPrinterNotShared = FALSE; DWORD MachineNameLength; DWORD RouterReturnValue = ROUTER_UNKNOWN; DWORD DesiredAccess; LPTSTR pcMark; BOOL bRemoteNameRequest = FALSE; BOOL bLocalCall = FALSE; #if DBG // // On DBG builds, force last error to zero so we can catch people // that don't set it when they should. // SetLastError( ERROR_SUCCESS ); #endif // // Reject "" - pointer to a NULL string. // if (pFullPrinterName && !pFullPrinterName[0]) { SetLastError(ERROR_INVALID_NAME); return ROUTER_UNKNOWN; } if (!pFullPrinterName) { return CreateServerHandle( pFullPrinterName, pPrinterHandle, pDefaults, pIniSpooler, PRINTER_HANDLE_SERVER ); } if( pFullPrinterName[0] == TEXT( '\\' ) && pFullPrinterName[1] == TEXT( '\\' )) { wcscpy(string, pFullPrinterName); if(pcMark = wcschr(string + 2, TEXT( '\\' ))) { *pcMark = TEXT('\0'); } if (MyName(string, pIniSpooler)) { // \\Server\Printer or \\Server if (!pcMark) { // \\Server return CreateServerHandle( pFullPrinterName, pPrinterHandle, pDefaults, pIniSpooler, PRINTER_HANDLE_SERVER ); } // Have \\Server\Printer, Set pPrinterName = Printer pPrinterName = pFullPrinterName + (pcMark - string) + 1; bRemoteNameRequest = TRUE; } } DBGMSG( DBG_TRACE, ( "OpenPrinter(%ws, %ws)\n", pFullPrinterName, pPrinterName )); bLocalCall = IsLocalCall(); EnterSplSem(); // // For the Mars folks who will come in with the same printer // connection, do a DeletePrinterCheck; this will allow // Mars connections that have been deleted to be proceed // to the Mars print providor // if (( pIniPrinter = FindPrinter( pPrinterName, pIniSpooler )) || ( pIniPrinter = FindPrinterShare( pPrinterName, pIniSpooler ))) { DeletePrinterCheck( pIniPrinter ); pIniPrinter = NULL; } // // The strategy for the rest of this code is to walk through each // different printer handle type, searching for a match. // // RouterReturnValue will be set to the current state of routing. // If a section recognizes and "owns" a printer and successfully // opens it, it sets RouterReturnValue to ROUTER_SUCCESS and // jumps to DoneRouting which allocs the handle. // // If it recoginzes the printer but fails to open it, and // guarentees that no one else (localspl code or other providers) // will recognize it, it should set RouterReturnValue to // ROUTER_STOP_ROUTING. We will quit at this point. // // If it doesn't recognize the printer, set RouterReturnValue // to ROUTER_UNKNOWN and we will keep looking. // // // Try regular printer name: "My Printer" "TestPrinter." // RouterReturnValue = OpenLocalPrinterName( pPrinterName, pIniSpooler, &TypeofHandle, &pIniPrinter, &pIniPort, &pIniNetPort, &hPort, &OpenPortError, pDefaults ); if( RouterReturnValue != ROUTER_UNKNOWN ){ if( bRemoteNameRequest ){ // // On success, determine whether the user is remote or local. // Note: we only do this for fully qualified names // (\\server\share), since using just the share or printer // name can only succeed locally. // if (bLocalCall) { if( (pIniSpooler->SpoolerFlags & SPL_REMOTE_HANDLE_CHECK) && (pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER) ) TypeofHandle |= PRINTER_HANDLE_REMOTE_DATA; } else { if( pIniSpooler->SpoolerFlags & SPL_REMOTE_HANDLE_CHECK ) TypeofHandle |= PRINTER_HANDLE_REMOTE_DATA; TypeofHandle |= PRINTER_HANDLE_REMOTE_CALL; } // // This is a remote open. // // If the printer is not shared, ensure the caller // has Administer access to the printer. // // The following seems to belong to the inside of the above "if" // clause. As it is, if an interactive user calls in with UNC name, // we require him to have ADMIN access if the printer is not shared; // but if he uses the printer friendly name, we let him go. // if( pIniPrinter && !( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED )){ bRemoteUserPrinterNotShared = TRUE; } } goto DoneRouting; } SPLASSERT( !TypeofHandle && !pIniPrinter && !pIniPort && !pIniNetPort && !pIniJob && !hPort ); // // Try LocalPrinter with an extention e.g. // // PortName, Port // PrinterName, Job xxxx // PrinterName, LocalOnlyToken // PrinterName, LocalsplOnlyToken // // See if the name includes a comma. Look for qualifiers: // Port Job LocalOnly LocalsplOnly // wcscpy( string, pPrinterName ); if( pSecondPart = wcschr( string, L',' )){ DWORD dwError; UINT uType; // // Turn into 2 strings // First PrintName // pSecondPart points to the rest. // *pSecondPart++ = 0; // // Get rid of Leading Spaces // while ( *pSecondPart == L' ' && *pSecondPart != 0 ) { pSecondPart++; } SPLASSERT( *pSecondPart ); // // PrintName, {LocalOnly|LocalsplOnly} // RouterReturnValue = CheckPrinterTokens( string, pSecondPart, &TypeofHandle, pIniSpooler, &pIniPrinter, &pIniPort, &pIniNetPort, &hPort, &OpenPortError, pDefaults ); if( RouterReturnValue != ROUTER_UNKNOWN ){ goto DoneRouting; } SPLASSERT( !TypeofHandle && !pIniPrinter && !pIniPort && !pIniNetPort && !pIniJob && !hPort ); // // PortName, Port // RouterReturnValue = CheckPrinterPortToken( string, pSecondPart, &TypeofHandle, &pIniPrinter, &pIniPort, &pIniJob, pDefaults, pIniSpooler ); if( RouterReturnValue != ROUTER_UNKNOWN ){ goto DoneRouting; } SPLASSERT( !TypeofHandle && !pIniPrinter && !pIniPort && !pIniNetPort && !pIniJob && !hPort ); // // PrinterName, Job ### // RouterReturnValue = CheckPrinterJobToken( string, pSecondPart, &TypeofHandle, &pIniPrinter, &pIniJob, &hReadFile, pIniSpooler ); if( RouterReturnValue != ROUTER_UNKNOWN ){ goto DoneRouting; } SPLASSERT( !TypeofHandle && !pIniPrinter && !pIniPort && !pIniNetPort && !pIniJob && !hPort ); // // "\\Server\,XcvPort Object" or ",XcvPort Object" // "\\Server\,XcvMonitor Object" or ",XcvMonitor Object" // // Verify that we're looking at the right server if (bRemoteNameRequest || *pPrinterName == L',') { RouterReturnValue = CheckXcvPortToken( pSecondPart, &TypeofHandle, pDefaults, pIniSpooler, pPrinterHandle ); } else { RouterReturnValue = ROUTER_UNKNOWN; } goto WrapUp; } // // We have completed all routing. Anything other than success // should exit now. // DoneRouting: if( RouterReturnValue == ROUTER_SUCCESS) { // // It's an error if the printer is pending deletion or pending creation. // SPLASSERT( pIniPrinter ); if (!pIniPrinter || (pIniPrinter->Status & PRINTER_PENDING_DELETION) && (pIniSpooler->SpoolerFlags & SPL_FAIL_OPEN_PRINTERS_PENDING_DELETION) && (pIniPrinter->cJobs == 0) || (pIniPrinter->Status & PRINTER_PENDING_CREATION)) { RouterReturnValue = ROUTER_STOP_ROUTING; SetLastError( ERROR_INVALID_PRINTER_NAME ); goto DoneRouting; } // // When the printer is opened, access type may be specified in // pDefaults. If no defaults are supplied (or request access // is unspecified), we use PRINTER_ACCESS_USE. // // Future calls with the handle will check against both the // current user privileges on this printer but also this initial // access. (Even if the user is an admin of the printer, unless // they open the printer with PRINTER_ALL_ACCESS, they can't // administer it.) // // If the user requires more access, the printer must be reopened. // if( !pDefaults || !pDefaults->DesiredAccess ){ if( TypeofHandle & PRINTER_HANDLE_JOB ){ DesiredAccess = JOB_READ; } else { DesiredAccess = PRINTER_READ; } } else { DesiredAccess = pDefaults->DesiredAccess; } // // If the user is remote and the printer is not shared, only allow // administrators succeed. // // This allows administrators to admin printers even if they // are not shared, and prevents non-admins from opening non-shared // printers. // if( bRemoteUserPrinterNotShared && !(DesiredAccess & PRINTER_ACCESS_ADMINISTER )) { PSPOOL pSpool; // Get a quick and dirty pSpool to pass in pSpool = (PSPOOL)AllocSplMem( SPOOL_SIZE ); if( pSpool == NULL ) { DBGMSG( DBG_WARNING, ("SplOpenPrinter failed to allocate memory %d\n", GetLastError() )); RouterReturnValue = ROUTER_STOP_ROUTING; goto WrapUp; } pSpool->signature = SJ_SIGNATURE; pSpool->pIniPrinter = pIniPrinter; // Add admin request, and see if user has the right. DesiredAccess |= PRINTER_ACCESS_ADMINISTER; if( !ValidateObjectAccess( SPOOLER_OBJECT_PRINTER, DesiredAccess, pSpool, &pSpool->GrantedAccess, pIniSpooler )) { SetLastError(ERROR_ACCESS_DENIED); RouterReturnValue = ROUTER_STOP_ROUTING; } DesiredAccess &= ~PRINTER_ACCESS_ADMINISTER; // clean up FreeSplMem( pSpool ); // If the user had no ADMIN privilege, fail the open call. if( RouterReturnValue == ROUTER_STOP_ROUTING ) goto WrapUp; } // // Create the printer handle that we will return to the user. // if( pFullPrinterName != pPrinterName) { wcsncpy( string, pFullPrinterName, (size_t) (pPrinterName - pFullPrinterName - 1)); string[pPrinterName - pFullPrinterName - 1] = L'\0'; } else { wcscpy(string, pIniSpooler->pMachineName); } *pPrinterHandle = CreatePrinterHandle( pFullPrinterName, string, pIniPrinter, pIniPort, pIniNetPort, pIniJob, TypeofHandle, hPort, pDefaults, pIniSpooler, DesiredAccess, pSplClientInfo, dwLevel, hReadFile ); if( *pPrinterHandle ){ // // Update the OpenPortError. // ((PSPOOL)*pPrinterHandle)->OpenPortError = OpenPortError; } else { SPLASSERT( GetLastError( )); RouterReturnValue = ROUTER_STOP_ROUTING; } } WrapUp: LeaveSplSem(); // // Don't have an SplOutSem as we could be called recursively. // switch( RouterReturnValue ){ case ROUTER_SUCCESS: DBGMSG( DBG_TRACE, ("OpenPrinter returned handle %x\n", *pPrinterHandle)); SPLASSERT( *pPrinterHandle ); break; case ROUTER_UNKNOWN: SPLASSERT( !TypeofHandle && !pIniPrinter && !pIniPort && !pIniNetPort && !pIniJob && !hPort ); // // hPort should not be valid. If it is, we have leaked a handle. // SPLASSERT( !hPort ); SPLASSERT( hReadFile == INVALID_HANDLE_VALUE ); DBGMSG( DBG_TRACE, ( "OpenPrinter failed, invalid name "TSTR"\n", pFullPrinterName )); SetLastError( ERROR_INVALID_NAME ); break; case ROUTER_STOP_ROUTING: LastError = GetLastError(); SPLASSERT( LastError ); // // On failure, we may have opened a port or file handle. We need // to close it since we won't return a valid handle, and // so ClosePrinter will never get called. // if( hPort != INVALID_PORT_HANDLE ) { ClosePrinter( hPort ); } if ( pIniJob && (pIniJob->hFileItem == INVALID_HANDLE_VALUE) ) { if ( hReadFile != INVALID_HANDLE_VALUE ) { CloseHandle( hReadFile ); hReadFile = INVALID_HANDLE_VALUE; } } DBGMSG( DBG_TRACE, ("OpenPrinter "TSTR" failed: Error %d\n", pFullPrinterName, GetLastError())); SetLastError( LastError ); break; } return RouterReturnValue; } BOOL SplClosePrinter( HANDLE hPrinter ) { PSPOOL pSpool=(PSPOOL)hPrinter; PSPOOL *ppIniSpool = NULL; PINISPOOLER pIniSpoolerDecRef = NULL; PSPLMAPVIEW pSplMapView; PMAPPED_JOB pMappedJob; BOOL bValid; DWORD Position; // // Allow us to close zombied handles. // EnterSplSem(); pSpool->Status &= ~SPOOL_STATUS_ZOMBIE; if (pSpool->TypeofHandle & PRINTER_HANDLE_XCV_PORT) { bValid = ValidateXcvHandle(pSpool->pIniXcv); } else { bValid = ValidateSpoolHandle(pSpool, 0); } LeaveSplSem(); if( !bValid ){ return FALSE; } if (!(pSpool->TypeofHandle & PRINTER_HANDLE_JOB) && pSpool->pIniJob && (pSpool->Status & SPOOL_STATUS_ADDJOB)) { LocalScheduleJob(hPrinter, pSpool->pIniJob->JobId); } if (pSpool->Status & SPOOL_STATUS_STARTDOC) { // it looks as though this might cause a double // decrement of pIniJob->cRef once inside LocalEndDocPrinter // and the other later in this routine. LocalEndDocPrinter(hPrinter); } if (pSpool->TypeofHandle & PRINTER_HANDLE_JOB) { if (pSpool->TypeofHandle & PRINTER_HANDLE_DIRECT) { // // If EndDoc is still waiting for a final ReadPrinter // if (pSpool->pIniJob->cbBuffer) { // Amount last transmitted // // Wake up the EndDoc Thread // SetEvent(pSpool->pIniJob->WaitForRead); SplOutSem(); // // Wait until he is finished // WaitForSingleObject(pSpool->pIniJob->WaitForWrite, INFINITE); EnterSplSem(); // // Now it is ok to close the handles // if (!CloseHandle(pSpool->pIniJob->WaitForWrite)) { DBGMSG(DBG_WARNING, ("CloseHandle failed %d %d\n", pSpool->pIniJob->WaitForWrite, GetLastError())); } if (!CloseHandle(pSpool->pIniJob->WaitForRead)) { DBGMSG(DBG_WARNING, ("CloseHandle failed %d %d\n", pSpool->pIniJob->WaitForRead, GetLastError())); } pSpool->pIniJob->WaitForRead = NULL; pSpool->pIniJob->WaitForWrite = NULL; LeaveSplSem(); } DBGMSG(DBG_TRACE, ("ClosePrinter(DIRECT):cRef = %d\n", pSpool->pIniJob->cRef)); } EnterSplSem(); DBGMSG(DBG_TRACE, ("ClosePrinter:cRef = %d\n", pSpool->pIniJob->cRef)); DECJOBREF(pSpool->pIniJob); DeleteJobCheck(pSpool->pIniJob); LeaveSplSem(); } // Unmap all views of the spool file and close file mapping handles while (pSplMapView = pSpool->pSplMapView) { pSpool->pSplMapView = pSplMapView->pNext; if (pSplMapView->pStartMapView) { UnmapViewOfFile( (LPVOID) pSplMapView->pStartMapView); } // CreateFileMapping returns NULL (not INVALID_HANDLE_VALUE) for failure if (pSplMapView->hMapSpoolFile) { CloseHandle(pSplMapView->hMapSpoolFile); } FreeSplMem(pSplMapView); } // Delete all mapped spool files that are not required EnterSplSem(); while (pMappedJob = pSpool->pMappedJob) { pSpool->pMappedJob = pMappedJob->pNext; if (!pSpool->pIniPrinter || !FindJob(pSpool->pIniPrinter, pMappedJob->JobId, &Position)) { // The job is gone and we have to delete the spool file LeaveSplSem(); // // This may need looking at for File Pooling // DeleteFile(pMappedJob->pszSpoolFile); EnterSplSem(); if (pSpool->pIniPrinter) { vMarkOff( pSpool->pIniPrinter->pIniSpooler->hJobIdMap, pMappedJob->JobId ); } } FreeSplMem(pMappedJob->pszSpoolFile); FreeSplMem(pMappedJob); } LeaveSplSem(); if ( pSpool->hReadFile != INVALID_HANDLE_VALUE ) { // Move the file pointer to the number of bytes committed and set the end of // file. if ((pSpool->pIniJob->Status & JOB_TYPE_OPTIMIZE) && SetFilePointer(pSpool->hReadFile, pSpool->pIniJob->dwValidSize, NULL, FILE_BEGIN) != 0xffffffff) { SetEndOfFile(pSpool->hReadFile); } // // File pooling Change, we close the file handle if we aren't file // pooling and we reset the seek pointer if we are file pooling. // if (pSpool->pIniJob) { if (pSpool->pIniJob->hFileItem == INVALID_HANDLE_VALUE) { if ( !CloseHandle( pSpool->hReadFile ) ) { DBGMSG(DBG_WARNING, ("ClosePrinter CloseHandle(%d) failed %d\n", pSpool->hReadFile, GetLastError())); } } else { // // People call ClosePrinter / OpenPrinter in sequence to be able // to read from the beginning of the spool file again. To get the // same effect, we need to set the seek pointer back to the // beginning of the hReadFile. // DWORD rc = ERROR_SUCCESS; rc = SetFilePointer(pSpool->hReadFile, 0, NULL, FILE_BEGIN); if (rc != ERROR_SUCCESS) { DBGMSG(DBG_WARNING, ("ClosePrinter SetFilePointer(%p) failed %d\n", pSpool->hReadFile, rc)); } } } } // // Close the handle that was opened via OpenPrinterPort: // if (pSpool->hPort) { if (pSpool->hPort != INVALID_PORT_HANDLE) { ClosePrinter(pSpool->hPort); } else { DBGMSG(DBG_WARNING, ("ClosePrinter ignoring bad port handle.\n")); } } EnterSplSem(); // // Remove us from the linked list of handles: // if (pSpool->TypeofHandle & PRINTER_HANDLE_PRINTER) { SPLASSERT( pSpool->pIniPrinter->signature == IP_SIGNATURE ); ppIniSpool = &pSpool->pIniPrinter->pSpool; } else if ((pSpool->TypeofHandle & PRINTER_HANDLE_SERVER) || (pSpool->TypeofHandle & PRINTER_HANDLE_XCV_PORT)) { SPLASSERT( pSpool->pIniSpooler->signature == ISP_SIGNATURE ); if (pSpool->TypeofHandle & PRINTER_HANDLE_XCV_PORT) XcvClose(pSpool->pIniXcv); pIniSpoolerDecRef = pSpool->pIniSpooler; ppIniSpool = &pSpool->pIniSpooler->pSpool; } if (ppIniSpool) { while (*ppIniSpool && *ppIniSpool != pSpool) ppIniSpool = &(*ppIniSpool)->pNext; if (*ppIniSpool) *ppIniSpool = pSpool->pNext; else { DBGMSG( DBG_WARNING, ( "Didn't find pSpool %08x in linked list\n", pSpool ) ); } } if (pSpool->pIniPrinter) { DECPRINTERREF( pSpool->pIniPrinter ); DeletePrinterCheck(pSpool->pIniPrinter); } DeletePrinterHandle(pSpool); if( pIniSpoolerDecRef ){ DECSPOOLERREF( pIniSpoolerDecRef ); } LeaveSplSem(); // // Don't call SplOutSem() since SplAddPrinter calls // use from inside the critical section. // return TRUE; } #ifdef _HYDRA_ ULONG GetClientSessionId( ) /*++ Routine Description: Get the SessionID from the client who we should be impersonating. If any errors, we return 0 to mean the Console SessionId since this may be a remote network call. Arguments: Return Value: SessionID of the effective token --*/ { BOOL Result; HANDLE TokenHandle; ULONG SessionId, ReturnLength; // // We should be impersonating the client, so we will get the // SessionId from out token. // // We may not have a valid one if this is a remote network // connection. Result = OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, FALSE, // Use impersonation &TokenHandle ); if( Result ) { // // Query the SessionID from the token added by HYDRA // Result = GetTokenInformation( TokenHandle, (TOKEN_INFORMATION_CLASS)TokenSessionId, &SessionId, sizeof(SessionId), &ReturnLength ); if( !Result ) { SessionId = 0; // Default to console } CloseHandle( TokenHandle ); } else { SessionId = 0; } return( SessionId ); } #endif