/*++ Copyright (c) 1993 Microsoft Corporation Module Name: nwspl.c Abstract: This module contains the Netware print provider. Author: Yi-Hsin Sung (yihsins) 15-Apr-1993 Revision History: Yi-Hsin Sung (yihsins) 15-May-1993 Moved most of the functionality to the server side Ram Viswanathan (ramv) 09-Aug-1995 Added functionality to Add and Delete Printer. --*/ #include #include #include #include #include #include #include #include #include #include #include //------------------------------------------------------------------ // // Local Functions // //------------------------------------------------------------------ // now all SKUs have TerminalServer flag. If App Server is enabled, SingleUserTS flag is cleared #define IsTerminalServer() (BOOLEAN)(!(USER_SHARED_DATA->SuiteMask & (1 << SingleUserTS))) //user mode DWORD InitializePortNames( VOID ); VOID NwpGetUserInfo( LPWSTR *ppszUser, BOOL *pfGateway ); DWORD NwpGetThreadUserInfo( LPWSTR *ppszUser, LPWSTR *ppszUserSid ); DWORD NwpGetUserNameFromSid( PSID pUserSid, LPWSTR *ppszUserName ); DWORD NwpGetLogonUserInfo( LPWSTR *ppszUserSid ); DWORD ThreadIsInteractive( VOID ); VOID pFreeAllContexts(); //------------------------------------------------------------------ // // Global Variables // //------------------------------------------------------------------ HMODULE hmodNW = NULL; BOOL fIsWinnt = FALSE ; WCHAR *pszRegistryPath = NULL; WCHAR *pszRegistryPortNames=L"PortNames"; WCHAR szMachineName[MAX_COMPUTERNAME_LENGTH + 3]; PNWPORT pNwFirstPort = NULL; CRITICAL_SECTION NwSplSem; CRITICAL_SECTION NwServiceListCriticalSection; // Used to protect linked // list of registered services HANDLE NwServiceListDoneEvent = NULL;// Used to stop local advertise // threads. STATIC HANDLE handleDummy; // This is a dummy handle used to // return to the clients if we have previously // opened the given printer successfully // and the netware workstation service is not // currently available. STATIC PRINTPROVIDOR PrintProvidor = { OpenPrinter, SetJob, GetJob, EnumJobs, AddPrinter, // NOT SUPPORTED DeletePrinter, // NOT SUPPORTED SetPrinter, GetPrinter, EnumPrinters, AddPrinterDriver, // NOT SUPPORTED EnumPrinterDrivers, // NOT SUPPORTED GetPrinterDriverW, // NOT SUPPORTED GetPrinterDriverDirectory, // NOT SUPPORTED DeletePrinterDriver, // NOT SUPPORTED AddPrintProcessor, // NOT SUPPORTED EnumPrintProcessors, // NOT SUPPORTED GetPrintProcessorDirectory, // NOT SUPPORTED DeletePrintProcessor, // NOT SUPPORTED EnumPrintProcessorDatatypes,// NOT SUPPORTED StartDocPrinter, StartPagePrinter, // NOT SUPPORTED WritePrinter, EndPagePrinter, // NOT SUPPORTED AbortPrinter, ReadPrinter, // NOT SUPPORTED EndDocPrinter, AddJob, ScheduleJob, GetPrinterData, // NOT SUPPORTED SetPrinterData, // NOT SUPPORTED WaitForPrinterChange, ClosePrinter, AddForm, // NOT SUPPORTED DeleteForm, // NOT SUPPORTED GetForm, // NOT SUPPORTED SetForm, // NOT SUPPORTED EnumForms, // NOT SUPPORTED EnumMonitors, // NOT SUPPORTED EnumPorts, AddPort, // NOT SUPPORTED ConfigurePort, DeletePort, CreatePrinterIC, // NOT SUPPORTED PlayGdiScriptOnPrinterIC, // NOT SUPPORTED DeletePrinterIC, // NOT SUPPORTED AddPrinterConnection, // NOT SUPPORTED DeletePrinterConnection, // NOT SUPPORTED PrinterMessageBox, // NOT SUPPORTED AddMonitor, // NOT SUPPORTED DeleteMonitor // NOT SUPPORTED }; //------------------------------------------------------------------ // // Initialization Functions // //------------------------------------------------------------------ BOOL InitializeDll( HINSTANCE hdll, DWORD dwReason, LPVOID lpReserved ) { NT_PRODUCT_TYPE ProductType ; UNREFERENCED_PARAMETER( lpReserved ); if ( dwReason == DLL_PROCESS_ATTACH ) { DisableThreadLibraryCalls( hdll ); hmodNW = hdll; // // are we a winnt machine? // fIsWinnt = RtlGetNtProductType(&ProductType) ? (ProductType == NtProductWinNt) : FALSE ; // // Initialize the critical section for maintaining the registered // service list // InitializeCriticalSection( &NwServiceListCriticalSection ); NwServiceListDoneEvent = CreateEventA( NULL, TRUE, FALSE, NULL ); } else if ( dwReason == DLL_PROCESS_DETACH ) { // // Free up memories used by the port link list // DeleteAllPortEntries(); // // Get rid of Service List and Shutdown SAP library // NwTerminateServiceProvider(); #ifndef NT1057 // // Clean up shell extensions // NwCleanupShellExtensions(); #endif pFreeAllContexts(); // clean up RNR stuff DeleteCriticalSection( &NwServiceListCriticalSection ); if ( NwServiceListDoneEvent ) { CloseHandle( NwServiceListDoneEvent ); NwServiceListDoneEvent = NULL; } } return TRUE; } DWORD InitializePortNames( VOID ) /*++ Routine Description: This is called by the InitializePrintProvidor to initialize the ports names used in this providor. Arguments: None. Return Value: Returns NO_ERROR or the error that occurred. --*/ { DWORD err; HKEY hkeyPath; HKEY hkeyPortNames; err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, pszRegistryPath, 0, KEY_READ, &hkeyPath ); if ( !err ) { err = RegOpenKeyEx( hkeyPath, pszRegistryPortNames, 0, KEY_READ, &hkeyPortNames ); if ( !err ) { DWORD i = 0; WCHAR Buffer[MAX_PATH]; DWORD BufferSize; while ( !err ) { BufferSize = sizeof(Buffer) / sizeof(WCHAR); err = RegEnumValue( hkeyPortNames, i, Buffer, &BufferSize, NULL, NULL, NULL, NULL ); if ( !err ) CreatePortEntry( Buffer ); i++; } /* We expect RegEnumKeyEx to return ERROR_NO_MORE_ITEMS * when it gets to the end of the keys, so reset the status: */ if( err == ERROR_NO_MORE_ITEMS ) err = NO_ERROR; RegCloseKey( hkeyPortNames ); } #if DBG else { IF_DEBUG(PRINT) KdPrint(("NWSPL [RegOpenKeyEx] (%ws) failed: Error = %d\n", pszRegistryPortNames, err )); } #endif RegCloseKey( hkeyPath ); } #if DBG else { IF_DEBUG(PRINT) KdPrint(("NWSPL [RegOpenKeyEx] (%ws) failed: Error = %d\n", pszRegistryPath, err )); } #endif return err; } //------------------------------------------------------------------ // // Print Provider Functions supported by NetWare provider // //------------------------------------------------------------------ BOOL InitializePrintProvidor( LPPRINTPROVIDOR pPrintProvidor, DWORD cbPrintProvidor, LPWSTR pszFullRegistryPath ) /*++ Routine Description: This is called by the spooler subsystem to initialize the print providor. Arguments: pPrintProvidor - Pointer to the print providor structure to be filled in by this function cbPrintProvidor - Count of bytes of the print providor structure pszFullRegistryPath - Full path to the registry key of this print providor Return Value: Always TRUE. --*/ { // // dfergus 20 Apr 2001 #323700 // Prevent Multiple CS Initialization // static int iCSInit = 0; DWORD dwLen; if ( !pPrintProvidor || !pszFullRegistryPath || !*pszFullRegistryPath ) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } memcpy( pPrintProvidor, &PrintProvidor, min( sizeof(PRINTPROVIDOR), cbPrintProvidor) ); // // Store the registry path for this print providor // if ( !(pszRegistryPath = AllocNwSplStr(pszFullRegistryPath)) ) return FALSE; // // Store the local machine name // szMachineName[0] = szMachineName[1] = L'\\'; dwLen = MAX_COMPUTERNAME_LENGTH; GetComputerName( szMachineName + 2, &dwLen ); #if DBG IF_DEBUG(PRINT) { KdPrint(("NWSPL [InitializePrintProvidor] ")); KdPrint(("RegistryPath = %ws, ComputerName = %ws\n", pszRegistryPath, szMachineName )); } #endif // // dfergus 20 Apr 2001 #323700 // Prevent Multiple CS Initialization // if( !iCSInit ) { InitializeCriticalSection( &NwSplSem ); iCSInit = 1; } // // Ignore the error returned from InitializePortNames. // The provider can still function if we cannot get all the port // names. // InitializePortNames(); return TRUE; } BOOL OpenPrinterW( LPWSTR pszPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTS pDefault ) /*++ Routine Description: This routine retrieves a handle identifying the specified printer. Arguments: pszPrinterName - Name of the printer phPrinter - Receives the handle that identifies the given printer pDefault - Points to a PRINTER_DEFAULTS structure. Can be NULL. Return Value: TRUE if the function succeeds, FALSE otherwise. Use GetLastError() for extended error information. --*/ { DWORD err; #if DBG IF_DEBUG(PRINT) KdPrint(( "NWSPL [OpenPrinter] Name = %ws\n", pszPrinterName )); #endif UNREFERENCED_PARAMETER( pDefault ); if ( !pszPrinterName ) { SetLastError( ERROR_INVALID_NAME ); return FALSE; } RpcTryExcept { err = NwrOpenPrinter( NULL, pszPrinterName, PortKnown( pszPrinterName ), (LPNWWKSTA_PRINTER_CONTEXT) phPrinter ); // // Make sure there is a port of this name so that // EnumPorts will return it. // if ( !err ) { if ( !PortExists( pszPrinterName, &err ) && !err ) { // // We will ignore the errors since it is // still OK if we can't add the port. // Cannot delete once created, don't create // We should not create port entry and registry entry if ( CreatePortEntry( pszPrinterName ) ) CreateRegistryEntry( pszPrinterName ); } } } RpcExcept(1) { DWORD code = RpcExceptionCode(); if ( code == RPC_S_SERVER_UNAVAILABLE ) { if ( PortKnown( pszPrinterName )) { *phPrinter = &handleDummy; err = NO_ERROR; } else { err = ERROR_INVALID_NAME; } } else { err = NwpMapRpcError( code ); } } RpcEndExcept if ( err ) { SetLastError( err ); #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [OpenPrinter] err = %d\n", err)); #endif } return ( err == NO_ERROR ); } BOOL ClosePrinter( HANDLE hPrinter ) /*++ Routine Description: This routine closes the given printer object. Arguments: hPrinter - Handle of the printer object Return Value: TRUE if the function succeeds, FALSE otherwise. Use GetLastError() for extended error information. --*/ { DWORD err; #if DBG IF_DEBUG(PRINT) KdPrint(( "NWSPL [ClosePrinter]\n")); #endif // // Just return success if the handle is a dummy one // if ( hPrinter == &handleDummy ) return TRUE; RpcTryExcept { err = NwrClosePrinter( (LPNWWKSTA_PRINTER_CONTEXT) &hPrinter ); } RpcExcept(1) { DWORD code = RpcExceptionCode(); if ( code == RPC_S_SERVER_UNAVAILABLE ) err = ERROR_INVALID_HANDLE; else err = NwpMapRpcError( code ); } RpcEndExcept if ( err ) SetLastError( err ); return err == NO_ERROR; } BOOL GetPrinter( HANDLE hPrinter, DWORD dwLevel, LPBYTE pbPrinter, DWORD cbBuf, LPDWORD pcbNeeded ) /*++ Routine Description: The routine retrieves information about the given printer. Arguments: hPrinter - Handle of the printer dwLevel - Specifies the level of the structure to which pbPrinter points. pbPrinter - Points to a buffer that receives the PRINTER_INFO object. cbBuf - Size, in bytes of the array pbPrinter points to. pcbNeeded - Points to a value which specifies the number of bytes copied if the function succeeds or the number of bytes required if cbBuf was too small. Return Value: TRUE if the function succeeds and FALSE otherwise. GetLastError() can be used to retrieve extended error information. --*/ { DWORD err; #if DBG IF_DEBUG(PRINT) KdPrint(( "NWSPL [GetPrinter] Level = %d\n", dwLevel )); #endif if ( hPrinter == &handleDummy ) { SetLastError( ERROR_NO_NETWORK ); return FALSE; } else if ( ( dwLevel != 1 ) && ( dwLevel != 2 ) && (dwLevel != 3 )) { SetLastError( ERROR_INVALID_LEVEL ); return FALSE; } RpcTryExcept { err = NwrGetPrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter, dwLevel, pbPrinter, cbBuf, pcbNeeded ); if ( !err ) { if ( dwLevel == 1 ) MarshallUpStructure( pbPrinter, PrinterInfo1Offsets, pbPrinter); else MarshallUpStructure( pbPrinter, PrinterInfo2Offsets, pbPrinter); } } RpcExcept(1) { DWORD code = RpcExceptionCode(); if ( code == RPC_S_SERVER_UNAVAILABLE ) err = ERROR_INVALID_HANDLE; else err = NwpMapRpcError( code ); } RpcEndExcept if ( err ) SetLastError( err ); return err == NO_ERROR; } BOOL SetPrinter( HANDLE hPrinter, DWORD dwLevel, LPBYTE pbPrinter, DWORD dwCommand ) /*++ Routine Description: The routine sets the specified by pausing printing, resuming printing, or clearing all print jobs. Arguments: hPrinter - Handle of the printer dwLevel - Specifies the level of the structure to which pbPrinter points. pbPrinter - Points to a buffer that supplies the PRINTER_INFO object. dwCommand - Specifies the new printer state. Return Value: TRUE if the function succeeds and FALSE otherwise. GetLastError() can be used to retrieve extended error information. --*/ { DWORD err = NO_ERROR; UNREFERENCED_PARAMETER( pbPrinter ); #if DBG IF_DEBUG(PRINT) { KdPrint(( "NWSPL [SetPrinter] Level = %d Command = %d\n", dwLevel, dwCommand )); } #endif if ( hPrinter == &handleDummy ) { SetLastError( ERROR_NO_NETWORK ); return FALSE; } switch ( dwLevel ) { case 0: case 1: case 2: case 3: break; default: SetLastError( ERROR_INVALID_LEVEL ); return FALSE; } RpcTryExcept { err = NwrSetPrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter, dwCommand ); } RpcExcept(1) { DWORD code = RpcExceptionCode(); if ( code == RPC_S_SERVER_UNAVAILABLE ) err = ERROR_INVALID_HANDLE; else err = NwpMapRpcError( code ); } RpcEndExcept if ( err ) SetLastError( err ); return err == NO_ERROR; } BOOL EnumPrintersW( DWORD dwFlags, LPWSTR pszName, DWORD dwLevel, LPBYTE pbPrinter, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned ) /*++ Routine Description: This routine enumerates the available providers, servers, printers depending on the given pszName. Arguments: dwFlags - Printer type requested pszName - The name of the container object dwLevel - The structure level requested pbPrinter - Points to the array to receive the PRINTER_INFO objects cbBuf - Size, in bytes of pbPrinter pcbNeeded - Count of bytes needed pcReturned - Count of PRINTER_INFO objects Return Value: TRUE if the function succeeds, FALSE otherwise. --*/ { DWORD err = NO_ERROR; #if DBG IF_DEBUG(PRINT) { KdPrint(("NWSPL [EnumPrinters] Flags = %d Level = %d",dwFlags,dwLevel)); if ( pszName ) KdPrint((" PrinterName = %ws\n", pszName )); else KdPrint(("\n")); } #endif if ( (dwLevel != 1) && (dwLevel != 2) ) { SetLastError( ERROR_INVALID_NAME ); // should be level, but winspool // is silly. return FALSE; } else if ( !pcbNeeded || !pcReturned ) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } RpcTryExcept { *pcReturned = 0; *pcbNeeded = 0; if ( ( dwFlags & PRINTER_ENUM_NAME ) && ( dwLevel == 1 ) ) { err = NwrEnumPrinters( NULL, pszName, pbPrinter, cbBuf, pcbNeeded, pcReturned ); if ( !err ) { DWORD i; for ( i = 0; i < *pcReturned; i++ ) MarshallUpStructure( pbPrinter + i*sizeof(PRINTER_INFO_1W), PrinterInfo1Offsets, pbPrinter ); } } else { err = ERROR_INVALID_NAME; } } RpcExcept(1) { DWORD code = RpcExceptionCode(); if ( code == RPC_S_SERVER_UNAVAILABLE ) err = ERROR_INVALID_NAME; else err = NwpMapRpcError( code ); } RpcEndExcept if ( err ) SetLastError( err ); return err == NO_ERROR; } // // Handle structure // This structure was copied from \nw\svcdlls\nwwks\server\spool.c // to fix NT bug # 366632. // typedef struct _NWSPOOL { DWORD nSignature; // Signature DWORD errOpenPrinter; // OpenPrinter API will always return // success on known printers. This will // contain the error that we get // if something went wrong in the API. PVOID pPrinter; // Points to the corresponding printer HANDLE hServer; // Opened handle to the server struct _NWSPOOL *pNextSpool; // Points to the next handle DWORD nStatus; // Status DWORD nJobNumber; // StartDocPrinter/AddJob: Job Number HANDLE hChangeEvent; // WaitForPrinterChange: event to wait on DWORD nWaitFlags; // WaitForPrinterChange: flags to wait on DWORD nChangeFlags; // Changes that occurred to the printer } NWSPOOL, *PNWSPOOL; DWORD StartDocPrinter( HANDLE hPrinter, DWORD dwLevel, LPBYTE lpbDocInfo ) /*++ Routine Description: This routine informs the print spooler that a document is to be spooled for printing. Arguments: hPrinter - Handle of the printer dwLevel - Level of the structure pointed to by lpbDocInfo. Must be 1. lpbDocInfo - Points to the DOC_INFO_1 object Return Value: TRUE if the function succeeds, FALSE otherwise. The extended error can be retrieved through GetLastError(). --*/ { DWORD err; DOC_INFO_1 *pDocInfo1 = (DOC_INFO_1 *) lpbDocInfo; LPWSTR pszUser = NULL; BOOL fGateway = FALSE; DWORD PrintOption = NW_GATEWAY_PRINT_OPTION_DEFAULT; LPWSTR pszPreferredSrv = NULL; #if DBG IF_DEBUG(PRINT) { KdPrint(( "NWSPL [StartDocPrinter] " )); if ( pDocInfo1 ) { if ( pDocInfo1->pDocName ) KdPrint(("Document %ws", pDocInfo1->pDocName )); if ( pDocInfo1->pOutputFile ) KdPrint(("OutputFile %ws", pDocInfo1->pOutputFile )); if ( pDocInfo1->pDatatype ) KdPrint(("Datatype %ws", pDocInfo1->pDatatype )); } KdPrint(("\n")); } #endif if ( hPrinter == &handleDummy ) { SetLastError( ERROR_NO_NETWORK ); return FALSE; } else if ( dwLevel != 1 ) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } // ignore the error, just use default value NwpGetUserInfo( &pszUser, &fGateway ); if ( !fGateway ) { NwQueryInfo( &PrintOption, &pszPreferredSrv ); if (pszPreferredSrv) { LocalFree( pszPreferredSrv ); } } RpcTryExcept { err = NwrStartDocPrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter, pDocInfo1? pDocInfo1->pDocName : NULL, pszUser, PrintOption, fGateway ); } RpcExcept(1) { DWORD code = RpcExceptionCode(); if ( code == RPC_S_SERVER_UNAVAILABLE ) err = ERROR_INVALID_HANDLE; else err = NwpMapRpcError( code ); } RpcEndExcept LocalFree( pszUser ); if ( err ) SetLastError( err ); // // Can't do this, seems to break GSWN printing on multi-homed machines // Commenting out code change that tries to return the job id. // // else // return ((PNWSPOOL) hPrinter)->nJobNumber; return err == NO_ERROR; } BOOL WritePrinter( HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcbWritten ) /*++ Routine Description: This routine informs the print spooler that the specified data should be written to the given printer. Arguments: hPrinter - Handle of the printer object pBuf - Address of array that contains printer data cbBuf - Size, in bytes of pBuf pcbWritten - Receives the number of bytes actually written to the printer Return Value: TRUE if it succeeds, FALSE otherwise. Use GetLastError() to get extended error. --*/ { DWORD err; #if DBG IF_DEBUG(PRINT) KdPrint(( "NWSPL [WritePrinter]\n")); if ( hPrinter == &handleDummy ) { SetLastError( ERROR_NO_NETWORK ); return FALSE; } #endif RpcTryExcept { err = NwrWritePrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter, pBuf, cbBuf, pcbWritten ); } RpcExcept(1) { DWORD code = RpcExceptionCode(); if ( code == RPC_S_SERVER_UNAVAILABLE ) err = ERROR_INVALID_HANDLE; else err = NwpMapRpcError( code ); } RpcEndExcept if ( err ) SetLastError( err ); return err == NO_ERROR; } BOOL AbortPrinter( HANDLE hPrinter ) /*++ Routine Description: This routine deletes a printer's spool file if the printer is configured for spooling. Arguments: hPrinter - Handle of the printer object Return Value: TRUE if the function succeeds, FALSE otherwise. --*/ { DWORD err; #if DBG IF_DEBUG(PRINT) KdPrint(( "NWSPL [AbortPrinter]\n")); if ( hPrinter == &handleDummy ) { SetLastError( ERROR_NO_NETWORK ); return FALSE; } #endif RpcTryExcept { err = NwrAbortPrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter ); } RpcExcept(1) { DWORD code = RpcExceptionCode(); if ( code == RPC_S_SERVER_UNAVAILABLE ) err = ERROR_INVALID_HANDLE; else err = NwpMapRpcError( code ); } RpcEndExcept if ( err ) SetLastError( err ); return err == NO_ERROR; } BOOL EndDocPrinter( HANDLE hPrinter ) /*++ Routine Description: This routine ends the print job for the given printer. Arguments: hPrinter - Handle of the printer object Return Value: TRUE if the function succeeds, FALSE otherwise. --*/ { DWORD err; #if DBG IF_DEBUG(PRINT) KdPrint(( "NWSPL [EndDocPrinter]\n")); #endif if ( hPrinter == &handleDummy ) { SetLastError( ERROR_NO_NETWORK ); return FALSE; } RpcTryExcept { err = NwrEndDocPrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter ); } RpcExcept(1) { DWORD code = RpcExceptionCode(); if ( code == RPC_S_SERVER_UNAVAILABLE ) err = ERROR_INVALID_HANDLE; else err = NwpMapRpcError( code ); } RpcEndExcept if ( err ) SetLastError( err ); return err == NO_ERROR; } BOOL GetJob( HANDLE hPrinter, DWORD dwJobId, DWORD dwLevel, LPBYTE pbJob, DWORD cbBuf, LPDWORD pcbNeeded ) /*++ Routine Description: This routine retrieves print-job data for the given printer. Arguments: hPrinter - Handle of the printer dwJobId - Job identifition number dwLevel - Data structure level of pbJob pbJob - Address of data-structure array cbBuf - Count of bytes in array pcbNeeded - Count of bytes retrieved or required Return Value: TRUE if the function succeeds, FALSE otherwise. --*/ { DWORD err; #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [GetJob] JobId = %d Level = %d\n", dwJobId, dwLevel)); #endif if ( hPrinter == &handleDummy ) { SetLastError( ERROR_NO_NETWORK ); return FALSE; } else if (( dwLevel != 1 ) && ( dwLevel != 2 )) { SetLastError( ERROR_INVALID_LEVEL ); return FALSE; } RpcTryExcept { err = NwrGetJob( (NWWKSTA_PRINTER_CONTEXT) hPrinter, dwJobId, dwLevel, pbJob, cbBuf, pcbNeeded ); if ( !err ) { if ( dwLevel == 1 ) MarshallUpStructure( pbJob, JobInfo1Offsets, pbJob ); else MarshallUpStructure( pbJob, JobInfo2Offsets, pbJob ); } } RpcExcept(1) { DWORD code = RpcExceptionCode(); if ( code == RPC_S_SERVER_UNAVAILABLE ) err = ERROR_INVALID_HANDLE; else err = NwpMapRpcError( code ); } RpcEndExcept if ( err ) SetLastError( err ); return err == NO_ERROR; } BOOL EnumJobs( HANDLE hPrinter, DWORD dwFirstJob, DWORD dwNoJobs, DWORD dwLevel, LPBYTE pbJob, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned ) /*++ Routine Description: This routine initializes the array of JOB_INFO_1 or JOB_INFO_2 structures with data describing the specified print jobs for the given printer. Arguments: hPrinter - Handle of the printer dwFirstJob - Location of first job in the printer dwNoJobs - Number of jobs to enumerate dwLevel - Data structure level pbJob - Address of structure array cbBuf - Size of pbJob, in bytes pcbNeeded - Receives the number of bytes copied or required pcReturned - Receives the number of jobs copied Return Value: TRUE if the function succeeds, FALSE otherwise. --*/ { DWORD err; #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [EnumJobs] Level = %d FirstJob = %d NoJobs = %d\n", dwLevel, dwFirstJob, dwNoJobs)); #endif if ( hPrinter == &handleDummy ) { SetLastError( ERROR_NO_NETWORK ); return FALSE; } else if ( ( dwLevel != 1 ) && ( dwLevel != 2 ) ) { SetLastError( ERROR_INVALID_LEVEL ); return FALSE; } RpcTryExcept { *pcReturned = 0; *pcbNeeded = 0; err = NwrEnumJobs( (NWWKSTA_PRINTER_CONTEXT) hPrinter, dwFirstJob, dwNoJobs, dwLevel, pbJob, cbBuf, pcbNeeded, pcReturned ); if ( !err ) { DWORD i; DWORD cbStruct; DWORD_PTR *pOffsets; if ( dwLevel == 1 ) { cbStruct = sizeof( JOB_INFO_1W ); pOffsets = JobInfo1Offsets; } else // dwLevel == 2 { cbStruct = sizeof( JOB_INFO_2W ); pOffsets = JobInfo2Offsets; } for ( i = 0; i < *pcReturned; i++ ) MarshallUpStructure( pbJob + i * cbStruct, pOffsets, pbJob ); } } RpcExcept(1) { DWORD code = RpcExceptionCode(); if ( code == RPC_S_SERVER_UNAVAILABLE ) err = ERROR_INVALID_HANDLE; else err = NwpMapRpcError( code ); } RpcEndExcept if ( err ) SetLastError( err ); return err == NO_ERROR; } BOOL SetJob( HANDLE hPrinter, DWORD dwJobId, DWORD dwLevel, LPBYTE pbJob, DWORD dwCommand ) /*++ Routine Description: This routine pauses, cancels, resumes, restarts the specified print job in the given printer. The function can also be used to set print job parameters such as job position, and so on. Arguments: hPrinter - Handle of the printer dwJobId - Job indentification number dwLevel - Data structure level pbJob - Address of data structure dwCommand - Specify the operation to be performed Return Value: TRUE if the function succeeds, FALSE otherwise. --*/ { DWORD err; #if DBG IF_DEBUG(PRINT) { KdPrint(("NWSPL [SetJob] Level = %d JobId = %d Command = %d\n", dwLevel, dwJobId, dwCommand)); } #endif if ( hPrinter == &handleDummy ) { SetLastError( ERROR_NO_NETWORK ); return FALSE; } else if ( ( dwLevel != 0 ) && ( dwLevel != 1 ) && ( dwLevel != 2 ) ) { SetLastError( ERROR_INVALID_LEVEL ); return FALSE; } else if ( ( dwLevel == 0 ) && ( pbJob != NULL ) ) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } RpcTryExcept { NW_JOB_INFO NwJobInfo; if ( dwLevel == 1 ) { NwJobInfo.nPosition = ((LPJOB_INFO_1W) pbJob)->Position; NwJobInfo.pUserName = ((LPJOB_INFO_1W) pbJob)->pUserName; NwJobInfo.pDocument = ((LPJOB_INFO_1W) pbJob)->pDocument; } else if ( dwLevel == 2 ) { NwJobInfo.nPosition = ((LPJOB_INFO_2W) pbJob)->Position; NwJobInfo.pUserName = ((LPJOB_INFO_2W) pbJob)->pUserName; NwJobInfo.pDocument = ((LPJOB_INFO_2W) pbJob)->pDocument; } err = NwrSetJob( (NWWKSTA_PRINTER_CONTEXT) hPrinter, dwJobId, dwLevel, dwLevel == 0 ? NULL : &NwJobInfo, dwCommand ); } RpcExcept(1) { DWORD code = RpcExceptionCode(); if ( code == RPC_S_SERVER_UNAVAILABLE ) err = ERROR_INVALID_HANDLE; else err = NwpMapRpcError( code ); } RpcEndExcept if ( err ) SetLastError( err ); return err == NO_ERROR; } BOOL AddJob( HANDLE hPrinter, DWORD dwLevel, LPBYTE pbAddJob, DWORD cbBuf, LPDWORD pcbNeeded ) /*++ Routine Description: This routine returns a full path and filename of a file that can be used to store a print job. Arguments: hPrinter - Handle of the printer dwLevel - Data structure level pbAddJob - Points to a ADD_INFO_1 structure cbBuf - Size of pbAddJob, in bytes pcbNeeded - Receives the bytes copied or required Return Value: TRUE if the function succeeds, FALSE otherwise. --*/ { DWORD err; ADDJOB_INFO_1W TempBuffer; PADDJOB_INFO_1W OutputBuffer; DWORD OutputBufferSize; #if DBG IF_DEBUG(PRINT) KdPrint(( "NWSPL [AddJob]\n")); #endif if ( hPrinter == &handleDummy ) { SetLastError( ERROR_NO_NETWORK ); return FALSE; } else if ( dwLevel != 1 ) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } // // The output buffer size must be at least the size of the fixed // portion of the structure marshalled by RPC or RPC will not // call the server-side to get the pcbNeeded. Use our own temporary // buffer to force RPC to call the server-side if output buffer // specified by the caller is too small. // if (cbBuf < sizeof(ADDJOB_INFO_1W)) { OutputBuffer = &TempBuffer; OutputBufferSize = sizeof(ADDJOB_INFO_1W); } else { OutputBuffer = (LPADDJOB_INFO_1W) pbAddJob; OutputBufferSize = cbBuf; } RpcTryExcept { err = NwrAddJob( (NWWKSTA_PRINTER_CONTEXT) hPrinter, OutputBuffer, OutputBufferSize, pcbNeeded ); } RpcExcept(1) { DWORD code = RpcExceptionCode(); if ( code == RPC_S_SERVER_UNAVAILABLE ) err = ERROR_INVALID_HANDLE; else err = NwpMapRpcError( code ); } RpcEndExcept if ( err ) SetLastError( err ); return err == NO_ERROR; } BOOL ScheduleJob( HANDLE hPrinter, DWORD dwJobId ) /*++ Routine Description: This routine informs the print spooler that the specified job can be scheduled for spooling. Arguments: hPrinter - Handle of the printer dwJobId - Job number that can be scheduled Return Value: TRUE if the function succeeds, FALSE otherwise. --*/ { DWORD err; #if DBG IF_DEBUG(PRINT) KdPrint(( "NWSPL [ScheduleJob] JobId = %d\n", dwJobId )); #endif if ( hPrinter == &handleDummy ) { SetLastError( ERROR_NO_NETWORK ); return FALSE; } RpcTryExcept { err = NwrScheduleJob( (NWWKSTA_PRINTER_CONTEXT) hPrinter, dwJobId ); } RpcExcept(1) { DWORD code = RpcExceptionCode(); if ( code == RPC_S_SERVER_UNAVAILABLE ) err = ERROR_INVALID_HANDLE; else err = NwpMapRpcError( code ); } RpcEndExcept if ( err ) SetLastError( err ); return err == NO_ERROR; } DWORD WaitForPrinterChange( HANDLE hPrinter, DWORD dwFlags ) /*++ Routine Description: This function returns when one or more requested changes occur on a print server or if the function times out. Arguments: hPrinter - Handle of the printer to wait on dwFlags - A bitmask that specifies the changes that the application wishes to be notified of. Return Value: Return a bitmask that indicates the changes that occurred. --*/ { DWORD err; #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [WaitForPrinterChange] Flags = %d\n", dwFlags)); #endif if ( hPrinter == &handleDummy ) { SetLastError( ERROR_NO_NETWORK ); return 0; } RpcTryExcept { err = NwrWaitForPrinterChange( (NWWKSTA_PRINTER_CONTEXT) hPrinter, &dwFlags ); } RpcExcept(1) { DWORD code = RpcExceptionCode(); if ( code == RPC_S_SERVER_UNAVAILABLE ) err = ERROR_INVALID_HANDLE; else err = NwpMapRpcError( code ); } RpcEndExcept if ( err ) { SetLastError( err ); return 0; } return dwFlags; } BOOL EnumPortsW( LPWSTR pszName, DWORD dwLevel, LPBYTE pbPort, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned ) /*++ Routine Description: This function enumerates the ports available for printing on a specified server. Arguments: pszName - Name of the server to enumerate on dwLevel - Structure level pbPort - Address of array to receive the port information cbBuf - Size, in bytes, of pbPort pcbNeeded - Address to store the number of bytes needed or copied pcReturned - Address to store the number of entries copied Return Value: TRUE if the function succeeds, FALSE otherwise. --*/ { DWORD err = NO_ERROR; DWORD cb = 0; PNWPORT pNwPort; LPPORT_INFO_1W pPortInfo1; LPBYTE pEnd = pbPort + cbBuf; LPBYTE pFixedDataEnd = pbPort; BOOL FitInBuffer; #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [EnumPorts]\n")); #endif if ( dwLevel != 1 ) { SetLastError( ERROR_INVALID_NAME ); return FALSE; } else if ( !IsLocalMachine( pszName ) ) { SetLastError( ERROR_INVALID_NAME ); return FALSE; } EnterCriticalSection( &NwSplSem ); pNwPort = pNwFirstPort; while ( pNwPort ) { cb += sizeof(PORT_INFO_1W) + ( wcslen( pNwPort->pName)+1)*sizeof(WCHAR); pNwPort = pNwPort->pNext; } *pcbNeeded = cb; *pcReturned = 0; if ( cb <= cbBuf ) { pEnd = pbPort + cbBuf; pNwPort = pNwFirstPort; while ( pNwPort ) { pPortInfo1 = (LPPORT_INFO_1W) pFixedDataEnd; pFixedDataEnd += sizeof( PORT_INFO_1W ); FitInBuffer = NwlibCopyStringToBuffer( pNwPort->pName, wcslen( pNwPort->pName), (LPCWSTR) pFixedDataEnd, (LPWSTR *) &pEnd, &pPortInfo1->pName ); ASSERT( FitInBuffer ); pNwPort = pNwPort->pNext; (*pcReturned)++; } } else { err = ERROR_INSUFFICIENT_BUFFER; } LeaveCriticalSection( &NwSplSem ); if ( err ) SetLastError( err ); return err == NO_ERROR; } BOOL DeletePortW( LPWSTR pszName, HWND hWnd, LPWSTR pszPortName ) /*++ Routine Description: This routine deletes the port given on the server. A dialog can be displayed if needed. Arguments: pszName - Name of the server for which the port should be deleted hWnd - Parent window pszPortName - The name of the port to delete Return Value: TRUE if the function succeeds, FALSE otherwise. --*/ { DWORD err; BOOL fPortDeleted; #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [DeletePort]\n")); #endif if ( !IsLocalMachine( pszName ) ) { SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } fPortDeleted = DeletePortEntry( pszPortName ); if ( fPortDeleted ) { err = DeleteRegistryEntry( pszPortName ); } else { err = ERROR_UNKNOWN_PORT; } if ( err ) SetLastError( err ); return err == NO_ERROR; } BOOL ConfigurePortW( LPWSTR pszName, HWND hWnd, LPWSTR pszPortName ) /*++ Routine Description: This routine displays the port configuration dialog box for the given port on the given server. Arguments: pszName - Name of the server on which the given port exist hWnd - Parent window pszPortName - The name of the port to be configured Return Value: TRUE if the function succeeds, FALSE otherwise. --*/ { DWORD nCurrentThreadId; DWORD nWindowThreadId; WCHAR szCaption[MAX_PATH]; WCHAR szMessage[MAX_PATH]; #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [ConfigurePort] PortName = %ws\n", pszPortName)); #endif if ( !IsLocalMachine( pszName ) ) { SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } else if ( !PortKnown( pszPortName ) ) { SetLastError( ERROR_UNKNOWN_PORT ); return FALSE; } nCurrentThreadId = GetCurrentThreadId(); nWindowThreadId = GetWindowThreadProcessId( hWnd, NULL ); if ( !AttachThreadInput( nCurrentThreadId, nWindowThreadId, TRUE )) KdPrint(("[NWSPL] AttachThreadInput failed with %d.\n",GetLastError())); if ( LoadStringW( hmodNW, IDS_NETWARE_PRINT_CAPTION, szCaption, sizeof( szCaption ) / sizeof( WCHAR ))) { if ( LoadStringW( hmodNW, IDS_NOTHING_TO_CONFIGURE, szMessage, sizeof( szMessage ) / sizeof( WCHAR ))) { MessageBox( hWnd, szMessage, szCaption, MB_OK | MB_ICONINFORMATION ); } else { KdPrint(("[NWSPL] LoadString failed with %d.\n",GetLastError())); } } else { KdPrint(("[NWSPL] LoadString failed with %d.\n",GetLastError())); } if ( !AttachThreadInput( nCurrentThreadId, nWindowThreadId, FALSE )) KdPrint(("[NWSPL] DetachThreadInput failed with %d.\n",GetLastError())); return TRUE; } //------------------------------------------------------------------ // // Print Provider Functions not supported by NetWare provider // //------------------------------------------------------------------ BOOL AddPrinterConnectionW( LPWSTR pszPrinterName ) { #if DBG IF_DEBUG(PRINT) { KdPrint(("NWSPL [AddPrinterConnection] PrinterName = %ws\n", pszPrinterName)); } #endif SetLastError( ERROR_INVALID_NAME ); return FALSE; } BOOL EnumMonitorsW( LPWSTR pszName, DWORD dwLevel, LPBYTE pbMonitor, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [EnumMonitors]\n")); #endif SetLastError( ERROR_INVALID_NAME ); return FALSE; } BOOL AddPortW( LPWSTR pszName, HWND hWnd, LPWSTR pszMonitorName ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [AddPort]\n")); #endif SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } HANDLE AddPrinterW( LPWSTR pszName, DWORD dwLevel, LPBYTE pbPrinter ) // Creates a print queue on the netware server and returns a handle to it { #ifdef NOT_USED #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [AddPrinterW]\n")); #endif SetLastError(ERROR_NOT_SUPPORTED); return FALSE; #else LPTSTR pszPrinterName = NULL; LPTSTR pszPServer = NULL; LPTSTR pszQueue = NULL; HANDLE hPrinter = NULL; DWORD err; PPRINTER_INFO_2 pPrinterInfo; #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [AddPrinterW]\n")); #endif pPrinterInfo = (PPRINTER_INFO_2)pbPrinter; if (dwLevel != 2) { err = ERROR_INVALID_PARAMETER; goto ErrorExit; } if (!(pszPrinterName = (LPTSTR)LocalAlloc(LPTR, (wcslen(((PRINTER_INFO_2 *)pbPrinter)->pPrinterName)+1)* sizeof(WCHAR)))) { err = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } wcscpy(pszPrinterName,pPrinterInfo->pPrinterName); // PrinterName is the name represented as \\server\share //The pszPServer parameter could have multiple fields separated by semicolons if ( ( !ValidateUNCName( pszPrinterName ) ) || ( (pszQueue = wcschr( pszPrinterName + 2, L'\\')) == NULL ) || ( pszQueue == (pszPrinterName + 2) ) || ( *(pszQueue + 1) == L'\0' ) ) { err = ERROR_INVALID_NAME; goto ErrorExit; } #if DBG IF_DEBUG(PRINT) KdPrint(( "NWSPL [AddPrinter] Name = %ws\n",pszPServer)); #endif if ( pszPrinterName == NULL ) //PrinterName is a mandatory field but not the list of PServers { #if DBG IF_DEBUG(PRINT) KdPrint(( "NWSPL [AddPrinter] Printername not supplied\n" )); #endif SetLastError( ERROR_INVALID_NAME ); goto ErrorExit; } //Check to see if there is a port of the same name // If so, abort the addprinter operation. // This code was commented earlier if (PortExists(pszPrinterName, &err ) && !err ) { #if DBG IF_DEBUG(PRINT) KdPrint(( "NWSPL [AddPrinter], = %ws; Port exists with same name\n", pszPrinterName )); #endif SetLastError(ERROR_ALREADY_ASSIGNED); goto ErrorExit; } // Put all the relevant information into the PRINTER_INFO_2 structure RpcTryExcept { err = NwrAddPrinter ( NULL, (LPPRINTER_INFO_2W) pPrinterInfo, (LPNWWKSTA_PRINTER_CONTEXT) &hPrinter ); if (!err) { #if DBG IF_DEBUG(PRINT) KdPrint(( "NWSPL [AddPrinter] Name = %ws\n", pszPrinterName )); #endif goto ErrorExit; } } RpcExcept(1) { DWORD code = RpcExceptionCode(); err = NwpMapRpcError( code ); goto ErrorExit; } RpcEndExcept if ( !pszPrinterName) (void) LocalFree((HLOCAL)pszPrinterName); return hPrinter; ErrorExit: if ( !pszPrinterName) (void) LocalFree((HLOCAL)pszPrinterName); SetLastError( err); return (HANDLE)0x0; #endif // #ifdef NOT_USED } BOOL DeletePrinter( HANDLE hPrinter ) { #ifdef NOT_USED #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [DeletePrinter]\n")); #endif SetLastError(ERROR_NOT_SUPPORTED); return FALSE; #else LPWSTR pszPrinterName = NULL ; // Used to delete entry from registry DWORD err = NO_ERROR; DWORD DoesPortExist; #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [DeletePrinter]\n")); #endif pszPrinterName = (LPWSTR)LocalAlloc(LPTR,sizeof(WCHAR)*MAX_PATH); if(pszPrinterName == NULL) { err = ERROR_NOT_ENOUGH_MEMORY; return FALSE; } // // Just return success if the handle is a dummy one // if ( hPrinter == &handleDummy ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [DeletePrinter] Dummy handle \n")); #endif SetLastError(ERROR_NO_NETWORK); return FALSE; } RpcTryExcept { err = NwrDeletePrinter( NULL, // pszPrinterName, (LPNWWKSTA_PRINTER_CONTEXT) &hPrinter ); } RpcExcept(1) { DWORD code = RpcExceptionCode(); if ( code == RPC_S_SERVER_UNAVAILABLE ) err = ERROR_INVALID_HANDLE; else err = NwpMapRpcError( code ); } RpcEndExcept if (!err && PortExists(pszPrinterName, &DoesPortExist) && DoesPortExist) { if ( DeleteRegistryEntry (pszPrinterName)) (void) DeletePortEntry(pszPrinterName); } if ( err ) SetLastError( err ); return err == NO_ERROR; #endif // #ifdef NOT_USED } BOOL DeletePrinterConnectionW( LPWSTR pszName ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [DeletePrinterConnection]\n")); #endif SetLastError( ERROR_INVALID_NAME ); return FALSE; } BOOL AddPrinterDriverW( LPWSTR pszName, DWORD dwLevel, LPBYTE pbPrinter ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [AddPrinterDriver]\n")); #endif SetLastError( ERROR_INVALID_NAME ); return FALSE; } BOOL EnumPrinterDriversW( LPWSTR pszName, LPWSTR pszEnvironment, DWORD dwLevel, LPBYTE pbDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [EnumPrinterDrivers]\n")); #endif SetLastError( ERROR_INVALID_NAME ); return FALSE; } BOOL GetPrinterDriverW( HANDLE hPrinter, LPWSTR pszEnvironment, DWORD dwLevel, LPBYTE pbDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [GetPrinterDriver]\n")); #endif SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } BOOL GetPrinterDriverDirectoryW( LPWSTR pszName, LPWSTR pszEnvironment, DWORD dwLevel, LPBYTE pbDriverDirectory, DWORD cbBuf, LPDWORD pcbNeeded ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [GetPrinterDriverDirectory]\n")); #endif SetLastError( ERROR_INVALID_NAME ); return FALSE; } BOOL DeletePrinterDriverW( LPWSTR pszName, LPWSTR pszEnvironment, LPWSTR pszDriverName ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [DeletePrinterDriver]\n")); #endif SetLastError( ERROR_INVALID_NAME ); return FALSE; } BOOL AddPrintProcessorW( LPWSTR pszName, LPWSTR pszEnvironment, LPWSTR pszPathName, LPWSTR pszPrintProcessorName ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [AddPrintProcessor]\n")); #endif SetLastError( ERROR_INVALID_NAME ); return FALSE; } BOOL EnumPrintProcessorsW( LPWSTR pszName, LPWSTR pszEnvironment, DWORD dwLevel, LPBYTE pbPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [EnumPrintProcessors]\n")); #endif SetLastError( ERROR_INVALID_NAME ); return FALSE; } BOOL EnumPrintProcessorDatatypesW( LPWSTR pszName, LPWSTR pszPrintProcessorName, DWORD dwLevel, LPBYTE pbDatatypes, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [EnumPrintProcessorDatatypes]\n")); #endif SetLastError( ERROR_INVALID_NAME ); return FALSE; } BOOL GetPrintProcessorDirectoryW( LPWSTR pszName, LPWSTR pszEnvironment, DWORD dwLevel, LPBYTE pbPrintProcessorDirectory, DWORD cbBuf, LPDWORD pcbNeeded ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [GetPrintProcessorDirectory]\n")); #endif SetLastError( ERROR_INVALID_NAME ); return FALSE; } BOOL StartPagePrinter( HANDLE hPrinter ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [StartPagePrinter]\n")); #endif SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } BOOL EndPagePrinter( HANDLE hPrinter ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [EndPagePrinter]\n")); #endif SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } BOOL ReadPrinter( HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcbRead ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [ReadPrinter]\n")); #endif SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } DWORD GetPrinterDataW( HANDLE hPrinter, LPWSTR pszValueName, LPDWORD pdwType, LPBYTE pbData, DWORD cbBuf, LPDWORD pcbNeeded ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [GetPrinterData]\n")); #endif SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } DWORD SetPrinterDataW( HANDLE hPrinter, LPWSTR pszValueName, DWORD dwType, LPBYTE pbData, DWORD cbData ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [SetPrinterData]\n")); #endif SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } BOOL AddForm( HANDLE hPrinter, DWORD dwLevel, LPBYTE pbForm ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [AddForm]\n")); #endif SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } BOOL DeleteFormW( HANDLE hPrinter, LPWSTR pszFormName ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [DeleteForm]\n")); #endif SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } BOOL GetFormW( HANDLE hPrinter, LPWSTR pszFormName, DWORD dwLevel, LPBYTE pbForm, DWORD cbBuf, LPDWORD pcbNeeded ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [GetForm]\n")); #endif SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } BOOL SetFormW( HANDLE hPrinter, LPWSTR pszFormName, DWORD dwLevel, LPBYTE pbForm ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [SetForm]\n")); #endif SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } BOOL EnumForms( HANDLE hPrinter, DWORD dwLevel, LPBYTE pbForm, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [EnumForms]\n")); #endif SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } HANDLE CreatePrinterIC( HANDLE hPrinter, LPDEVMODE pDevMode ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [CreatePrinterIC]\n")); #endif SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } BOOL PlayGdiScriptOnPrinterIC( HANDLE hPrinterIC, LPBYTE pbIn, DWORD cbIn, LPBYTE pbOut, DWORD cbOut, DWORD ul ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [PlayGdiScriptOnPrinterIC]\n")); #endif SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } BOOL DeletePrinterIC( HANDLE hPrinterIC ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [DeletePrinterIC]\n")); #endif SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } DWORD PrinterMessageBoxW( HANDLE hPrinter, DWORD dwError, HWND hWnd, LPWSTR pszText, LPWSTR pszCaption, DWORD dwType ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [PrinterMessageBox]\n")); #endif SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } BOOL AddMonitorW( LPWSTR pszName, DWORD dwLevel, LPBYTE pbMonitorInfo ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [AddMonitor]\n")); #endif SetLastError( ERROR_INVALID_NAME ); return FALSE; } BOOL DeleteMonitorW( LPWSTR pszName, LPWSTR pszEnvironment, LPWSTR pszMonitorName ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [DeleteMonitor]\n")); #endif SetLastError( ERROR_INVALID_NAME ); return FALSE; } BOOL DeletePrintProcessorW( LPWSTR pszName, LPWSTR pszEnvironment, LPWSTR pszPrintProcessorName ) { #if DBG IF_DEBUG(PRINT) KdPrint(("NWSPL [DeletePrintProcessor]\n")); #endif SetLastError( ERROR_INVALID_NAME ); return FALSE; } //------------------------------------------------------------------ // // Print Provider Miscellaneous Functions // //------------------------------------------------------------------ VOID NwpGetUserInfo( LPWSTR *ppszUser, BOOL *pfGateway ) /*++ Routine Description: Get the user information of the impersonating client. Arguments: ppszUser - A pointer to buffer to store the Unicode string if the impersonated client's user name can be looked up successfully. If the conversion was unsuccessful, it points to NULL. pfGateway - A pointer to a boolean to store whether the user is printing through a gateway or not. We assume that if the user sid and the current logon user sid is not the same, then the user is printing through gateway. Else the user is printing locally. Return Value: None. --*/ { DWORD err; LPWSTR pszUserSid = NULL; // LPWSTR pszLogonUserSid = NULL; //Removed for Multi-user code merge //Terminal Server doesn't user this varible //There is no single "Logon User" in Terminal Server // // If any error occurs while trying to get the username, just // assume no user name and not gateway printing. // *pfGateway = TRUE; *ppszUser = NULL; if ( ((err = NwpGetThreadUserInfo( ppszUser, &pszUserSid )) == NO_ERROR) // && ((err = NwpGetLogonUserInfo( &pszLogonUserSid ))) == NO_ERROR) //Removed from Multi-user code merge ) { if ( ThreadIsInteractive() ) { *pfGateway = FALSE; } else { *pfGateway = TRUE; } #if DBG IF_DEBUG(PRINT) KdPrint(("NwpGetUserInfo: Thread User= %ws, Thread SID = %ws,\nfGateway = %d\n", *ppszUser, pszUserSid, *pfGateway )); #endif // } else { // if ( _wcsicmp( pszUserSid, pszLogonUserSid ) == 0 ) { // *pfGateway = FALSE; // } //#if DBG // IF_DEBUG(PRINT) // KdPrint(("NwpGetUserInfo: Thread User= %ws, Thread SID = %ws,\nCurrent SID = %ws fGateway = %d\n", // *ppszUser, pszUserSid, pszLogonUserSid, *pfGateway )); //#endif // } LocalFree( pszUserSid ); } } #define SIZE_OF_TOKEN_INFORMATION \ sizeof( TOKEN_USER ) \ + sizeof( SID ) \ + sizeof( ULONG ) * SID_MAX_SUB_AUTHORITIES DWORD NwpGetThreadUserInfo( LPWSTR *ppszUser, LPWSTR *ppszUserSid ) /*++ Routine Description: Get the user name and user sid string of the impersonating client. Arguments: ppszUser - A pointer to buffer to store the Unicode string if the impersonated client's can be looked up. If the lookup was unsuccessful, it points to NULL. ppszUserSid - A pointer to buffer to store the string if the impersonated client's SID can be expanded successfully into unicode string. If the conversion was unsuccessful, it points to NULL. Return Value: The error code. --*/ { DWORD err; HANDLE TokenHandle; UCHAR TokenInformation[ SIZE_OF_TOKEN_INFORMATION ]; ULONG ReturnLength; *ppszUser = NULL; *ppszUserSid = NULL; // We can use OpenThreadToken because this server thread // is impersonating a client if ( !OpenThreadToken( GetCurrentThread(), TOKEN_READ, TRUE, /* Open as self */ &TokenHandle )) { #if DBG IF_DEBUG(PRINT) KdPrint(("NwpGetThreadUserInfo: OpenThreadToken failed: Error %d\n", GetLastError())); #endif return(GetLastError()); } // notice that we've allocated enough space for the // TokenInformation structure. so if we fail, we // return a NULL pointer indicating failure if ( !GetTokenInformation( TokenHandle, TokenUser, TokenInformation, sizeof( TokenInformation ), &ReturnLength )) { #if DBG IF_DEBUG(PRINT) KdPrint(("NwpGetThreadUserInfo: GetTokenInformation failed: Error %d\n", GetLastError())); #endif return(GetLastError()); } CloseHandle( TokenHandle ); // convert the Sid (pointed to by pSid) to its // equivalent Unicode string representation. err = NwpGetUserNameFromSid( ((PTOKEN_USER)TokenInformation)->User.Sid, ppszUser ); err = err? err : NwpConvertSid( ((PTOKEN_USER)TokenInformation)->User.Sid, ppszUserSid ); if ( err ) { if ( *ppszUser ) LocalFree( *ppszUser ); if ( *ppszUserSid ) LocalFree( *ppszUserSid ); } return err; } DWORD NwpGetUserNameFromSid( PSID pUserSid, LPWSTR *ppszUserName ) /*++ Routine Description: Lookup the user name given the user SID. Arguments: pUserSid - Points to the user sid to be looked up ppszUserName - A pointer to buffer to store the string if the impersonated client's SID can be expanded successfully into unicode string. If the conversion was unsuccessful, it points to NULL. Return Value: The error code. --*/ { NTSTATUS ntstatus; LSA_HANDLE hlsa; OBJECT_ATTRIBUTES oa; SECURITY_QUALITY_OF_SERVICE sqos; PLSA_REFERENCED_DOMAIN_LIST plsardl = NULL; PLSA_TRANSLATED_NAME plsatn = NULL; sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); sqos.ImpersonationLevel = SecurityImpersonation; sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; sqos.EffectiveOnly = FALSE; InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL ); oa.SecurityQualityOfService = &sqos; ntstatus = LsaOpenPolicy( NULL, &oa, POLICY_LOOKUP_NAMES, &hlsa ); if ( NT_SUCCESS( ntstatus )) { ntstatus = LsaLookupSids( hlsa, 1, &pUserSid, &plsardl, &plsatn ); if ( NT_SUCCESS( ntstatus )) { UNICODE_STRING *pUnicodeStr = &((*plsatn).Name); *ppszUserName = LocalAlloc( LMEM_ZEROINIT, pUnicodeStr->Length+sizeof(WCHAR)); if ( *ppszUserName != NULL ) { memcpy( *ppszUserName, pUnicodeStr->Buffer, pUnicodeStr->Length ); } else { ntstatus = STATUS_NO_MEMORY; } LsaFreeMemory( plsardl ); LsaFreeMemory( plsatn ); } #if DBG else { KdPrint(("NwpGetUserNameFromSid: LsaLookupSids failed: Error = %d\n", GetLastError())); } #endif LsaClose( hlsa ); } #if DBG else { KdPrint(("NwpGetUserNameFromSid: LsaOpenPolicy failed: Error = %d\n", GetLastError())); } #endif return RtlNtStatusToDosError( ntstatus ); } DWORD NwpGetLogonUserInfo( LPWSTR *ppszUserSid ) /*++ Routine Description: Get the logon user sid string from the registry. Arguments: ppszUserSid - On return, this points to the current logon user sid string. Return Value: The error code. --*/ { DWORD err; HKEY WkstaKey; LPWSTR CurrentUser = NULL; // // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services // \NWCWorkstation\Parameters to get the sid of the CurrentUser // err = RegOpenKeyExW( HKEY_LOCAL_MACHINE, NW_WORKSTATION_REGKEY, REG_OPTION_NON_VOLATILE, KEY_READ, &WkstaKey ); if ( err == NO_ERROR) { // // Read the current user SID string so that we // know which key is the current user key to open. // err = NwReadRegValue( WkstaKey, NW_CURRENTUSER_VALUENAME, &CurrentUser ); RegCloseKey( WkstaKey ); if ( err == NO_ERROR) { *ppszUserSid = CurrentUser; } } return(err); } #define SIZE_OF_STATISTICS_TOKEN_INFORMATION \ sizeof( TOKEN_STATISTICS ) DWORD ThreadIsInteractive( VOID ) /*++ Routine Description: Determines if this is an "Interactive" logon thread Arguments: none Return Value: TRUE - Thread is interactive FALSE - Thread is not interactive --*/ { HANDLE TokenHandle; UCHAR TokenInformation[ SIZE_OF_STATISTICS_TOKEN_INFORMATION ]; WCHAR LogonIdKeyName[NW_MAX_LOGON_ID_LEN]; ULONG ReturnLength; LUID LogonId; LONG RegError; HKEY InteractiveLogonKey; HKEY OneLogonKey; // We can use OpenThreadToken because this server thread // is impersonating a client if ( !OpenThreadToken( GetCurrentThread(), TOKEN_READ, TRUE, /* Open as self */ &TokenHandle )) { #if DBG IF_DEBUG(PRINT) KdPrint(("ThreadIsInteractive: OpenThreadToken failed: Error %d\n", GetLastError())); #endif return FALSE; } // notice that we've allocated enough space for the // TokenInformation structure. so if we fail, we // return a NULL pointer indicating failure if ( !GetTokenInformation( TokenHandle, TokenStatistics, TokenInformation, sizeof( TokenInformation ), &ReturnLength )) { #if DBG IF_DEBUG(PRINT) KdPrint(("ThreadIsInteractive: GetTokenInformation failed: Error %d\n", GetLastError())); #endif return FALSE; } CloseHandle( TokenHandle ); LogonId = ((PTOKEN_STATISTICS)TokenInformation)->AuthenticationId; RegError = RegOpenKeyExW( HKEY_LOCAL_MACHINE, NW_INTERACTIVE_LOGON_REGKEY, REG_OPTION_NON_VOLATILE, KEY_READ, &InteractiveLogonKey ); if (RegError != ERROR_SUCCESS) { #if DBG IF_DEBUG(PRINT) KdPrint(("ThreadIsInteractive: RegOpenKeyExW failed: Error %d\n", GetLastError())); #endif return FALSE; } NwLuidToWStr(&LogonId, LogonIdKeyName); // // Open the key under Logon // RegError = RegOpenKeyExW( InteractiveLogonKey, LogonIdKeyName, REG_OPTION_NON_VOLATILE, KEY_READ, &OneLogonKey ); if ( RegError == ERROR_SUCCESS ) { (void) RegCloseKey(OneLogonKey); (void) RegCloseKey(InteractiveLogonKey); return TRUE; /* We found it */ } else { (void) RegCloseKey(InteractiveLogonKey); return FALSE; /* We did not find it */ } } DWORD NwpCitrixGetUserInfo( LPWSTR *ppszUserSid ) /*++ Routine Description: Get the user sid string of the client. Arguments: ppszUserSid - A pointer to buffer to store the string. Return Value: The error code. --*/ { DWORD err; HANDLE TokenHandle; UCHAR TokenInformation[ SIZE_OF_TOKEN_INFORMATION ]; ULONG ReturnLength; *ppszUserSid = NULL; // We can use OpenThreadToken because this server thread // is impersonating a client if ( !OpenThreadToken( GetCurrentThread(), TOKEN_READ, TRUE, /* Open as self */ &TokenHandle )) { err = GetLastError(); if ( err == ERROR_NO_TOKEN ) { if ( !OpenProcessToken( GetCurrentProcess(), TOKEN_READ, &TokenHandle )) { #if DBG IF_DEBUG(PRINT) KdPrint(("NwpGetThreadUserInfo: OpenThreadToken failed: Error %d\n", GetLastError())); #endif return(GetLastError()); } } else return( err ); } // notice that we've allocated enough space for the // TokenInformation structure. so if we fail, we // return a NULL pointer indicating failure if ( !GetTokenInformation( TokenHandle, TokenUser, TokenInformation, sizeof( TokenInformation ), &ReturnLength )) { #if DBG IF_DEBUG(PRINT) KdPrint(("NwpGetThreadUserInfo: GetTokenInformation failed: Error %d\n", GetLastError())); #endif return(GetLastError()); } CloseHandle( TokenHandle ); // convert the Sid (pointed to by pSid) to its // equivalent Unicode string representation. err = NwpConvertSid( ((PTOKEN_USER)TokenInformation)->User.Sid, ppszUserSid ); if ( err ) { if ( *ppszUserSid ) LocalFree( *ppszUserSid ); } return err; }