/*++ Copyright (c) 1990 - 1995 Microsoft Corporation Module Name: prndata.c Abstract: This module provides all the public exported APIs relating to Printer and Job management for the Local Print Providor Author: Dave Snipp (DaveSn) 15-Mar-1991 Revision History: mattfe Apr 5 95 - we keep the driver data key open and then just do the read / write operations here. Steve Wilson (SWilson) Jan 11 96 - Added Server handle functionality to Get & SetPrinterData and pretty much changed everything in the process. Steve Wilson (SWilson) May 31 96 - Added SplEnumPrinterData and SplDeletePrinterData --*/ #include #define OPEN_PORT_TIMEOUT_VALUE 3000 // 3 seconds #define DELETE_PRINTER_DATA 0 #define SET_PRINTER_DATA 1 DWORD SetPrinterDataPrinter( HANDLE hPrinter, LPWSTR pValueName, DWORD Type, LPBYTE pData, DWORD cbData, BOOL bSet ); typedef enum { REG_PRINT, REG_PRINTERS, REG_PROVIDERS } REG_PRINT_KEY; DWORD GetServerKeyHandle( PINISPOOLER pIniSpooler, REG_PRINT_KEY eKey, HKEY *hPrintKey ); DWORD RegSetDefaultSpoolDirectory( LPWSTR pValueName, DWORD dwType, LPBYTE pData, DWORD cbData, HKEY hKey ); DWORD RegSetBasic( LPWSTR pValueName, DWORD dwType, LPBYTE pData, DWORD cbData, HKEY hKey ); DWORD NonRegGetSchedulerThreadPriorityDefault( PINISPOOLER pIniSpooler, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded ); DWORD NonRegGetPortThreadPriorityDefault( PINISPOOLER pIniSpooler, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded ); DWORD NonRegGetArchitecture( PINISPOOLER pIniSpooler, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded ); typedef struct { LPWSTR pValue; BOOL (*pSet) ( LPWSTR pValueName, DWORD dwType, LPBYTE pData, DWORD cbData, HKEY *hKey ); REG_PRINT_KEY eKey; } SERVER_DATA, *PSERVER_DATA; typedef struct { LPWSTR pValue; DWORD (*pGet)( PINISPOOLER pIniSpooler, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded ); } NON_REGISTRY_DATA, *PNON_REGISTRY_DATA; SERVER_DATA gpServerRegistry[] = {{SPLREG_DEFAULT_SPOOL_DIRECTORY, RegSetDefaultSpoolDirectory, REG_PRINTERS}, {SPLREG_PORT_THREAD_PRIORITY, RegSetBasic, REG_PRINT}, {SPLREG_SCHEDULER_THREAD_PRIORITY, RegSetBasic, REG_PRINT}, {SPLREG_BEEP_ENABLED, RegSetBasic, REG_PRINT}, {SPLREG_NET_POPUP, RegSetBasic, REG_PROVIDERS}, {SPLREG_EVENT_LOG, RegSetBasic, REG_PROVIDERS}, {SPLREG_MAJOR_VERSION, NULL, REG_PRINT}, {SPLREG_MINOR_VERSION, NULL, REG_PRINT}, {SPLREG_NO_REMOTE_PRINTER_DRIVERS, RegSetBasic, REG_PRINT}, {0,0,0}}; NON_REGISTRY_DATA gpNonRegistryData[] = {{SPLREG_PORT_THREAD_PRIORITY_DEFAULT, NonRegGetPortThreadPriorityDefault}, {SPLREG_SCHEDULER_THREAD_PRIORITY_DEFAULT, NonRegGetSchedulerThreadPriorityDefault}, {SPLREG_ARCHITECTURE, NonRegGetArchitecture}, {0,0}}; extern WCHAR *szPrinterData; BOOL AvailableBidiPort( PINIPORT pIniPort, PINIMONITOR pIniLangMonitor ) { // // File ports and ports with no monitor are useless // if ( (pIniPort->Status & PP_FILE) || !(pIniPort->Status & PP_MONITOR) ) return FALSE; // // If no LM then PM should support pfnGetPrinterDataFromPort // if ( !pIniLangMonitor && !pIniPort->pIniMonitor->fn.pfnGetPrinterDataFromPort ) return FALSE; // // A port with no jobs or same monitor is printing then it is ok // return !pIniPort->pIniJob || pIniLangMonitor == pIniPort->pIniLangMonitor; } DWORD GetPrinterDataFromPort( PINIPRINTER pIniPrinter, LPWSTR pszValueName, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded ) /*++ Routine Description: Tries to use GetPrinterDataFromPort monitor function to satisfy a GetPrinterData call Arguments: pIniPrinter - Points to an INIPRINTER Return Value: Win32 error code --*/ { DWORD rc = ERROR_INVALID_PARAMETER; DWORD i, dwFirstPortWithNoJobs, dwFirstPortHeld; PINIMONITOR pIniLangMonitor = NULL; PINIPORT pIniPort; SplInSem(); // // Is the printer bidi enabled with the LM supporting // pfnGetPrinterDataFromPort? (Note: even PM can support this function) // if ( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_ENABLE_BIDI ) { pIniLangMonitor = pIniPrinter->pIniDriver->pIniLangMonitor; SPLASSERT(pIniLangMonitor); if ( pIniLangMonitor && !pIniLangMonitor->fn.pfnGetPrinterDataFromPort ) pIniLangMonitor = NULL; } // // Initialize to max // dwFirstPortWithNoJobs = dwFirstPortHeld = pIniPrinter->cPorts; for ( i = 0 ; i < pIniPrinter->cPorts ; ++i ) { pIniPort = pIniPrinter->ppIniPorts[i]; // // Skip ports that can't be used // if ( !AvailableBidiPort(pIniPort, pIniLangMonitor) ) continue; // // Port does not need closing? // if ( pIniLangMonitor == pIniPort->pIniLangMonitor ) { // // If no jobs also then great let's use it // if ( !pIniPort->pIniJob ) goto PortFound; if ( dwFirstPortHeld == pIniPrinter->cPorts ) { dwFirstPortHeld = i; } } else if ( !pIniPort->pIniJob && dwFirstPortWithNoJobs == pIniPrinter->cPorts ) { dwFirstPortWithNoJobs = i; } } // // If all ports need closing as well as have jobs let's quit // if ( dwFirstPortWithNoJobs == pIniPrinter->cPorts && dwFirstPortHeld == pIniPrinter->cPorts ) { return rc; //Didn't leave CS and did not unset event } // // We will prefer a port with no jobs (even thought it requires closing) // if ( dwFirstPortWithNoJobs < pIniPrinter->cPorts ) pIniPort = pIniPrinter->ppIniPorts[dwFirstPortWithNoJobs]; else pIniPort = pIniPrinter->ppIniPorts[dwFirstPortHeld]; PortFound: SPLASSERT(AvailableBidiPort(pIniPort, pIniLangMonitor)); INCPORTREF(pIniPort); LeaveSplSem(); SplOutSem(); // // By unsetting the event for the duration of the GetPrinterDataFromPort // we make sure even if a job requiring different monitor got assigned // to the port it can't open/close the port. // // Since GetPrinterDataFromPort is supposed to come back fast it is ok // if ( WAIT_OBJECT_0 != WaitForSingleObject(pIniPort->hWaitToOpenOrClose, OPEN_PORT_TIMEOUT_VALUE) ) { DBGMSG(DBG_WARNING, ("GetPrinterDataFromPort: WaitForSingleObject timed-out\n")); goto CleanupFromOutsideSplSem; //Left CS did not unset the event } // // Port needs to be opened? // if ( pIniPort->pIniLangMonitor != pIniLangMonitor || !pIniPort->hPort ) { // // A job got assigned after we left the CS and before the event // was reset? // if ( pIniPort->pIniJob ) { SetEvent(pIniPort->hWaitToOpenOrClose); goto CleanupFromOutsideSplSem; //Outside CS did set event } EnterSplSem(); if ( !OpenMonitorPort(pIniPort, pIniLangMonitor, pIniPrinter->pName, FALSE) ) { SetEvent(pIniPort->hWaitToOpenOrClose); goto Cleanup; //Inside CS but already set the event } LeaveSplSem(); } SplOutSem(); if ( !pIniLangMonitor ) pIniLangMonitor = pIniPort->pIniMonitor; if ( (*pIniLangMonitor->fn.pfnGetPrinterDataFromPort)(pIniPort->hPort, 0, pszValueName, NULL, 0, (LPWSTR)pData, cbBuf, pcbNeeded) ) { rc = ERROR_SUCCESS; } else { rc = GetLastError(); } // // At this point we do not care if someone tries to open/close the port // we can set the event before entering the splsem // SetEvent(pIniPort->hWaitToOpenOrClose); CleanupFromOutsideSplSem: EnterSplSem(); Cleanup: SplInSem(); DECPORTREF(pIniPort); return rc; } DWORD SplGetPrinterData( HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded ) { PSPOOL pSpool=(PSPOOL)hPrinter; DWORD rc = ERROR_INVALID_HANDLE; PSERVER_DATA pRegistry; // points to table of Print Server registry entries PNON_REGISTRY_DATA pNonReg; HKEY hPrintKey; PINIPRINTER pIniPrinter; HKEY hKey; DWORD dwType; if (!ValidateSpoolHandle(pSpool, 0)) { return rc; } if (!pValueName || !pcbNeeded) { rc = ERROR_INVALID_PARAMETER; return rc; } if (pType) dwType = *pType; // pType may be NULL // Server Handle if (pSpool->TypeofHandle & PRINTER_HANDLE_SERVER) { // Check Registry Table for (pRegistry = gpServerRegistry ; pRegistry->pValue ; ++pRegistry) { if (!_wcsicmp(pRegistry->pValue, pValueName)) { if ((rc = GetServerKeyHandle(pSpool->pIniSpooler, pRegistry->eKey, &hPrintKey)) == ERROR_SUCCESS) { *pcbNeeded = nSize; rc = RegQueryValueEx(hPrintKey, pValueName, 0, pType, pData, pcbNeeded); RegCloseKey(hPrintKey); } break; } } if (!pRegistry->pValue) { // May be a non-registry entry for (pNonReg = gpNonRegistryData ; pNonReg->pValue ; ++pNonReg) { if (!_wcsicmp(pNonReg->pValue, pValueName)) { rc = (*pNonReg->pGet)(pSpool->pIniSpooler, &dwType, pData, nSize, pcbNeeded); if (pType) *pType = dwType; break; } } if (!pNonReg->pValue) { rc = ERROR_INVALID_PARAMETER; } } // Printer handle } else { EnterSplSem(); pIniPrinter = pSpool->pIniPrinter; SPLASSERT(pIniPrinter && pIniPrinter->signature == IP_SIGNATURE); if (pIniPrinter->Status & PRINTER_PENDING_CREATION) { LeaveSplSem(); rc = ERROR_INVALID_PRINTER_STATE; } else { SPLASSERT(pIniPrinter->hPrinterDataKey); if ( AccessGranted(SPOOLER_OBJECT_PRINTER, PRINTER_ACCESS_ADMINISTER, pSpool ) ) { rc = GetPrinterDataFromPort(pIniPrinter, pValueName, pData, nSize, pcbNeeded); } hKey = pSpool->pIniPrinter->hPrinterDataKey; LeaveSplSem(); if ( rc == ERROR_SUCCESS ) { *pType = REG_BINARY; (VOID)SetPrinterDataPrinter(hPrinter, pValueName, *pType, pData, *pcbNeeded, SET_PRINTER_DATA); } else if ( rc != ERROR_INSUFFICIENT_BUFFER ) { *pcbNeeded = nSize; rc = RegQueryValueEx(hKey, pValueName, 0, pType, pData, pcbNeeded); } } } SplOutSem(); return rc; } DWORD SplEnumPrinterData( HANDLE hPrinter, DWORD dwIndex, // index of value to query LPWSTR pValueName, // address of buffer for value string DWORD cbValueName, // size of buffer for value string LPDWORD pcbValueName, // address for size of value buffer LPDWORD pType, // address of buffer for type code LPBYTE pData, // address of buffer for value data DWORD cbData, // size of buffer for value data LPDWORD pcbData // address for size of data buffer ) { PSPOOL pSpool=(PSPOOL)hPrinter; DWORD rc = ERROR_INVALID_HANDLE; HKEY hKey; PINIPRINTER pIniPrinter; if (!ValidateSpoolHandle(pSpool, 0)) { return rc; } if (!pValueName || !pcbValueName) { rc = ERROR_INVALID_PARAMETER; return rc; } EnterSplSem(); pIniPrinter = pSpool->pIniPrinter; SPLASSERT(pIniPrinter && pIniPrinter->signature == IP_SIGNATURE); if (pIniPrinter->Status & PRINTER_PENDING_CREATION) { LeaveSplSem(); rc = ERROR_INVALID_PRINTER_STATE; } else { SPLASSERT(pIniPrinter->hPrinterDataKey); hKey = pSpool->pIniPrinter->hPrinterDataKey; LeaveSplSem(); if (!cbValueName && !cbData) { // Both sizes are NULL, so user wants to get buffer sizes rc = RegQueryInfoKey( hKey, // Key NULL, // lpClass NULL, // lpcbClass NULL, // lpReserved NULL, // lpcSubKeys NULL, // lpcbMaxSubKeyLen NULL, // lpcbMaxClassLen NULL, // lpcValues pcbValueName, // lpcbMaxValueNameLen pcbData, // lpcbMaxValueLen NULL, // lpcbSecurityDescriptor NULL // lpftLastWriteTime ); *pcbValueName = (*pcbValueName + 1)*sizeof(WCHAR); } else { *pcbValueName = cbValueName/sizeof(WCHAR); *pcbData = cbData; rc = RegEnumValue( hKey, dwIndex, pValueName, pcbValueName, NULL, pType, pData, pcbData ); *pcbValueName = (*pcbValueName + 1)*sizeof(WCHAR); } } return rc; } DWORD SplDeletePrinterData( HANDLE hPrinter, LPWSTR pValueName ) { PSPOOL pSpool = (PSPOOL)hPrinter; DWORD rc = ERROR_INVALID_HANDLE; if (!ValidateSpoolHandle(pSpool, 0)) { return rc; } rc = SetPrinterDataPrinter(hPrinter, pValueName, 0, NULL, 0, DELETE_PRINTER_DATA); return rc; } DWORD SplSetPrinterData( HANDLE hPrinter, LPWSTR pValueName, DWORD Type, LPBYTE pData, DWORD cbData ) { PSPOOL pSpool = (PSPOOL)hPrinter; DWORD rc = ERROR_INVALID_HANDLE; if (!ValidateSpoolHandle(pSpool, 0)) { return rc; } if (pSpool->TypeofHandle & PRINTER_HANDLE_SERVER) { if ( !ValidateObjectAccess( SPOOLER_OBJECT_SERVER, SERVER_ACCESS_ADMINISTER, NULL, pSpool->pIniSpooler)) { rc = ERROR_ACCESS_DENIED; } else { rc = SetPrinterDataServer(pSpool->pIniSpooler, pValueName, Type, pData, cbData); } } else { rc = SetPrinterDataPrinter(hPrinter, pValueName, Type, pData, cbData, SET_PRINTER_DATA); } return rc; } // SetPrinterDataServer - also called during initialization DWORD SetPrinterDataServer( PINISPOOLER pIniSpooler, LPWSTR pValueName, DWORD Type, LPBYTE pData, DWORD cbData ) { LPWSTR pKeyName; DWORD rc; HANDLE hToken; PINIPRINTER pIniPrinter; PINIJOB pIniJob; PSERVER_DATA pRegistry; // points to table of Print Server registry entries HKEY hKey; // Server Handle if (!pValueName) { rc = ERROR_INVALID_PARAMETER; } else { for (pRegistry = gpServerRegistry ; pRegistry->pValue ; ++pRegistry) { if (!_wcsicmp(pRegistry->pValue, pValueName)) { if ((rc = GetServerKeyHandle(pIniSpooler, pRegistry->eKey, &hKey)) == ERROR_SUCCESS) { hToken = RevertToPrinterSelf(); if (pRegistry->pSet) { rc = (*pRegistry->pSet)(pValueName, Type, pData, cbData, hKey); } else { rc = ERROR_INVALID_PARAMETER; } RegCloseKey(hKey); ImpersonatePrinterClient(hToken); } break; } } if (!pRegistry->pValue) { rc = ERROR_INVALID_PARAMETER; } } return rc; } DWORD SetPrinterDataPrinter( HANDLE hPrinter, LPWSTR pValueName, DWORD Type, LPBYTE pData, DWORD cbData, BOOL bSet // SET_PRINTER_DATA or DELETE_PRINTER_DATA ) { PSPOOL pSpool = (PSPOOL)hPrinter; LPWSTR pKeyName; DWORD rc = ERROR_INVALID_HANDLE; HANDLE hToken; PINIPRINTER pIniPrinter; PINIJOB pIniJob; if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )){ goto Done; } EnterSplSem(); pIniPrinter = pSpool->pIniPrinter; SPLASSERT(pIniPrinter && pIniPrinter->signature == IP_SIGNATURE && pIniPrinter->hPrinterDataKey); if ( !AccessGranted( SPOOLER_OBJECT_PRINTER, PRINTER_ACCESS_ADMINISTER, pSpool ) ) { rc = ERROR_ACCESS_DENIED; goto DoneFromSplSem; } hToken = RevertToPrinterSelf(); if (bSet == SET_PRINTER_DATA) { rc = RegSetValueEx(pIniPrinter->hPrinterDataKey, pValueName, 0, Type, pData, cbData ); } else { rc = RegDeleteValue(pIniPrinter->hPrinterDataKey, pValueName ); } ImpersonatePrinterClient(hToken); if ( rc == ERROR_SUCCESS ) { UpdatePrinterIni(pIniPrinter, CHANGEID_ONLY); SetPrinterChange(pIniPrinter, NULL, NULL, PRINTER_CHANGE_SET_PRINTER_DRIVER, pSpool->pIniSpooler ); } // // Now if there are any Jobs waiting for these changes because of // DevQueryPrint fix them as well // pIniJob = pIniPrinter->pIniFirstJob; while (pIniJob) { if (pIniJob->Status & JOB_BLOCKED_DEVQ) { pIniJob->Status &= ~JOB_BLOCKED_DEVQ; FreeSplStr(pIniJob->pStatus); pIniJob->pStatus = NULL; SetPrinterChange(pIniJob->pIniPrinter, pIniJob, NVJobStatusAndString, PRINTER_CHANGE_SET_JOB, pIniJob->pIniPrinter->pIniSpooler ); } pIniJob = pIniJob->pIniNextJob; } CHECK_SCHEDULER(); DoneFromSplSem: LeaveSplSem(); Done: return rc; } DWORD GetServerKeyHandle( PINISPOOLER pIniSpooler, REG_PRINT_KEY eKey, HKEY *hKey ) { DWORD rc; HANDLE hToken; hToken = RevertToPrinterSelf(); switch (eKey) { case REG_PRINT: rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, pIniSpooler->pszRegistryRoot, 0, KEY_ALL_ACCESS, hKey); break; case REG_PRINTERS: rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, pIniSpooler->pszRegistryPrinters, 0, KEY_ALL_ACCESS, hKey); break; case REG_PROVIDERS: rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, pIniSpooler->pszRegistryProviders, 0, KEY_ALL_ACCESS, hKey); break; default: rc = ERROR_INVALID_PARAMETER; break; } ImpersonatePrinterClient(hToken); return rc; } DWORD RegSetDefaultSpoolDirectory( LPWSTR pValueName, DWORD dwType, LPBYTE pData, DWORD cbData, HKEY hKey ) { DWORD rc; BOOL bStatus; SECURITY_ATTRIBUTES SecurityAttributes; // Create the directory with the proper security, or fail trying SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); SecurityAttributes.lpSecurityDescriptor = CreateEverybodySecurityDescriptor(); SecurityAttributes.bInheritHandle = FALSE; // CreateDirectory returns FALSE if directory already exists. bStatus = CreateDirectory((LPWSTR) pData, &SecurityAttributes); if (bStatus) { if (!RemoveDirectory((LPWSTR) pData)) { // Doesn't really matter if we can remove it or not DBGMSG(DBG_WARNING, ("RemoveDirectory failed: %ws %d\n", pData, GetLastError())); } } else { rc = GetLastError(); // Get last error on CreateDirectory failure if (rc != ERROR_ALREADY_EXISTS) { DBGMSG(DBG_WARNING, ("PRNDATA: Could not create default spool directory %ws %d\n", pData, rc)); } } if (bStatus || rc == ERROR_ALREADY_EXISTS) { rc = RegSetValueEx(hKey, pValueName, 0, dwType, pData, cbData); } LocalFree(SecurityAttributes.lpSecurityDescriptor); if (rc == ERROR_SUCCESS) { rc = ERROR_SUCCESS_RESTART_REQUIRED; } return rc; } DWORD RegSetBasic( LPWSTR pValueName, DWORD dwType, LPBYTE pData, DWORD cbData, HKEY hKey ) { BOOL rc; rc = RegSetValueEx(hKey, pValueName, 0, dwType, pData, cbData); if (rc == ERROR_SUCCESS) { rc = ERROR_SUCCESS_RESTART_REQUIRED; } return rc; } DWORD NonRegGetPortThreadPriorityDefault( PINISPOOLER pIniSpooler, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded ) { DWORD rc; if (nSize >= sizeof DEFAULT_PORT_THREAD_PRIORITY) { *pData = DEFAULT_PORT_THREAD_PRIORITY; *pcbNeeded = sizeof DEFAULT_PORT_THREAD_PRIORITY; rc = ERROR_SUCCESS; } else { *pcbNeeded = sizeof DEFAULT_PORT_THREAD_PRIORITY; rc = ERROR_MORE_DATA; } *pType = REG_DWORD; return rc; } DWORD NonRegGetSchedulerThreadPriorityDefault( PINISPOOLER pIniSpooler, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded ) { DWORD rc; if (nSize >= sizeof DEFAULT_SCHEDULER_THREAD_PRIORITY) { *pData = DEFAULT_SCHEDULER_THREAD_PRIORITY; *pcbNeeded = sizeof DEFAULT_SCHEDULER_THREAD_PRIORITY; rc = ERROR_SUCCESS; } else { *pcbNeeded = sizeof DEFAULT_SCHEDULER_THREAD_PRIORITY; rc = ERROR_MORE_DATA; } *pType = REG_DWORD; return rc; } DWORD NonRegGetArchitecture( PINISPOOLER pIniSpooler, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded ) { DWORD rc = ERROR_SUCCESS; DWORD cbName = 0; *pType = REG_SZ; cbName = wcslen((LPWSTR) szEnvironment)*sizeof(WCHAR) + sizeof(WCHAR); *pcbNeeded = cbName; if (*pcbNeeded <= nSize) { wcscpy((LPWSTR) pData, (LPWSTR) szEnvironment); rc = ERROR_SUCCESS; } else { rc = ERROR_MORE_DATA; } return rc; }