/*++ Copyright (c) 1993 Microsoft Corporation Module Name: spool.c Abstract: This module contains the Netware print provider. Author: Yi-Hsin Sung (yihsins) 15-May-1993 Revision History: --*/ #include #include #include #include #include #include #include #include #include #include //------------------------------------------------------------------ // // Local Definitions // //------------------------------------------------------------------ #define NW_SIGNATURE 0x574E /* "NW" is the signature */ #define SPOOL_STATUS_STARTDOC 0x00000001 #define SPOOL_STATUS_ADDJOB 0x00000002 #define SPOOL_STATUS_ABORT 0x00000003 #define PRINTER_CHANGE_VALID 0x55770F07 #define PRINTER_CHANGE_DEFAULT_TIMEOUT_VALUE 10000 #define PRINTER_CHANGE_MINIMUM_TIMEOUT_VALUE 1000 #define REG_TIMEOUT_PATH L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Parameters" #define REG_TIMEOUT_VALUE_NAME L"PrintNotifyTimeout" #define NDS_MAX_NAME_CHARS 256 #define NDS_MAX_NAME_SIZE ( NDS_MAX_NAME_CHARS * 2 ) // // Printer structure // typedef struct _NWPRINTER { LPWSTR pszServer; // Server Name LPWSTR pszQueue; // Queue Name LPWSTR pszUncConnection; // UNC Connection Name // (only present if NDS print queue DWORD nQueueId; // Queue Id struct _NWPRINTER *pNextPrinter; // Points to the next printer struct _NWSPOOL *pSpoolList; // Points to the list of open handles } NWPRINTER, *PNWPRINTER; // // Handle structure // 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. PNWPRINTER 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; //------------------------------------------------------------------ // // Global Variables // //------------------------------------------------------------------ // Stores the timeout value used in WaitForPrinterChange ( in milliseconds ) STATIC DWORD NwTimeOutValue = PRINTER_CHANGE_DEFAULT_TIMEOUT_VALUE; // Points to the link list of printers STATIC PNWPRINTER NwPrinterList = NULL; //------------------------------------------------------------------ // // Local Function Prototypes // //------------------------------------------------------------------ VOID NwSetPrinterChange( IN PNWSPOOL pSpool, IN DWORD nFlags ); PNWPRINTER NwFindPrinterEntry( IN LPWSTR pszServer, IN LPWSTR pszQueue ); DWORD NwCreatePrinterEntry( IN LPWSTR pszServer, IN LPWSTR pszQueue, OUT PNWPRINTER *ppPrinter, OUT PHANDLE phServer ); VOID NwRemovePrinterEntry( IN PNWPRINTER pPrinter ); LPWSTR NwGetUncObjectName( IN LPWSTR ContainerName ); VOID NwInitializePrintProvider( VOID ) /*++ Routine Description: This routine initializes the server side print provider when the workstation service starts up. Arguments: None. Return Value: --*/ { DWORD err; HKEY hkey; DWORD dwTemp; DWORD dwSize = sizeof( dwTemp ); // // Read the time out value from the registry. // We will ignore all errors since we can always have a default time out. // The default will be used if the key does not exist. // err = RegOpenKeyExW( HKEY_LOCAL_MACHINE, REG_TIMEOUT_PATH, 0, KEY_READ, &hkey ); if ( !err ) { err = RegQueryValueExW( hkey, REG_TIMEOUT_VALUE_NAME, NULL, NULL, (LPBYTE) &dwTemp, &dwSize ); if ( !err ) { NwTimeOutValue = dwTemp; // // tommye - bug 139469 - removed // if (NwTimeOutValue >= 0) because NwtimeOutValue is a DWORD // // Use the minimum timeout value if the // value set in the registry is too small. // if (NwTimeOutValue <= PRINTER_CHANGE_MINIMUM_TIMEOUT_VALUE) { NwTimeOutValue = PRINTER_CHANGE_MINIMUM_TIMEOUT_VALUE; } } RegCloseKey( hkey ); } } VOID NwTerminatePrintProvider( VOID ) /*++ Routine Description: This routine cleans up the server side print provider when the workstation service shut downs. Arguments: None. Return Value: --*/ { PNWPRINTER pPrinter, pNext; PNWSPOOL pSpool, pNextSpool; for ( pPrinter = NwPrinterList; pPrinter; pPrinter = pNext ) { pNext = pPrinter->pNextPrinter; pPrinter->pNextPrinter = NULL; for ( pSpool = pPrinter->pSpoolList; pSpool; pSpool = pNextSpool ) { pNextSpool = pSpool->pNextSpool; if ( pSpool->hChangeEvent ) CloseHandle( pSpool->hChangeEvent ); (VOID) NtClose( pSpool->hServer ); // // Free all memory associated with the context handle // FreeNwSplMem( pSpool, sizeof( NWSPOOL) ); } pPrinter->pSpoolList = NULL; FreeNwSplStr( pPrinter->pszServer ); FreeNwSplStr( pPrinter->pszQueue ); if ( pPrinter->pszUncConnection ) { (void) NwrDeleteConnection( NULL, pPrinter->pszUncConnection, FALSE ); FreeNwSplStr( pPrinter->pszUncConnection ); } FreeNwSplMem( pPrinter, sizeof( NWPRINTER)); } NwPrinterList = NULL; NwTimeOutValue = PRINTER_CHANGE_DEFAULT_TIMEOUT_VALUE; } DWORD NwrOpenPrinter( IN LPWSTR Reserved, IN LPWSTR pszPrinterName, IN DWORD fKnownPrinter, OUT LPNWWKSTA_PRINTER_CONTEXT phPrinter ) /*++ Routine Description: This routine retrieves a handle identifying the specified printer. Arguments: Reserved - Unused pszPrinterName - Name of the printer fKnownPrinter - TRUE if we have successfully opened the printer before, FALSE otherwise. phPrinter - Receives the handle that identifies the given printer Return Value: --*/ { DWORD err; PNWSPOOL pSpool = NULL; LPWSTR pszServer = NULL; LPWSTR pszQueue = NULL; PNWPRINTER pPrinter = NULL; BOOL fImpersonate = FALSE ; HANDLE hServer; BOOL isPrinterNameValid; UNREFERENCED_PARAMETER( Reserved ); if ( pszPrinterName[0] == L' ' && pszPrinterName[1] == L'\\' && pszPrinterName[2] == L'\\' ) { if ( (pszServer = AllocNwSplStr( pszPrinterName + 1 )) == NULL ) return ERROR_NOT_ENOUGH_MEMORY; isPrinterNameValid = ValidateUNCName( pszPrinterName + 1 ); } else { if ( (pszServer = AllocNwSplStr( pszPrinterName )) == NULL ) return ERROR_NOT_ENOUGH_MEMORY; isPrinterNameValid = ValidateUNCName( pszPrinterName ); } CharUpperW( pszServer ); // convert in place // // ValidatePrinterName // if ( ( !isPrinterNameValid ) || ( (pszQueue = wcschr( pszServer + 2, L'\\')) == NULL ) || ( pszQueue == (pszServer + 2) ) || ( *(pszQueue + 1) == L'\0' ) ) { FreeNwSplStr( pszServer ); return ERROR_INVALID_NAME; } *pszQueue = L'\0'; // put a '\0' in place of '\\' pszQueue++; // Get past the '\0' if ( !(pSpool = AllocNwSplMem( LMEM_ZEROINIT, sizeof( NWSPOOL)))) { err = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } // // Impersonate the client // if ((err = NwImpersonateClient()) != NO_ERROR) { goto ErrorExit; } fImpersonate = TRUE ; EnterCriticalSection( &NwPrintCritSec ); if ((err = NwCreatePrinterEntry( pszServer, pszQueue, &pPrinter, &hServer))) { if ( !fKnownPrinter ) { LeaveCriticalSection( &NwPrintCritSec ); goto ErrorExit; } } // // Construct the print queue context handle to give back to the caller // pSpool->nSignature = NW_SIGNATURE; pSpool->errOpenPrinter = err; pSpool->hServer = hServer; pSpool->nStatus = 0; pSpool->nJobNumber = 0; pSpool->hChangeEvent= NULL; pSpool->nWaitFlags = 0; pSpool->nChangeFlags= 0; if ( !err ) { pSpool->pPrinter = pPrinter; pSpool->pNextSpool = pPrinter->pSpoolList; pPrinter->pSpoolList= pSpool; } else { pSpool->pPrinter = NULL; pSpool->pNextSpool = NULL; } // We know about this printer before but failed to retrieve // it this time. Clean up the error and return successfully. // The error code is stored in the handle above which // will be returned on subsequent calls using this // dummy handle. err = NO_ERROR; LeaveCriticalSection( &NwPrintCritSec ); ErrorExit: if (fImpersonate) (void) NwRevertToSelf() ; if ( err ) { if ( pSpool ) FreeNwSplMem( pSpool, sizeof( NWSPOOL) ); } else { *phPrinter = (NWWKSTA_PRINTER_CONTEXT) pSpool; } // // Free up all allocated memories // *(pszServer + wcslen( pszServer)) = L'\\'; FreeNwSplStr( pszServer ); return err; } DWORD NwrClosePrinter( IN OUT LPNWWKSTA_PRINTER_CONTEXT phPrinter ) /*++ Routine Description: This routine closes the given printer object. Arguments: phPrinter - Handle of the printer object Return Value: --*/ { PNWSPOOL pSpool = (PNWSPOOL) *phPrinter; PNWPRINTER pPrinter; PNWSPOOL pCur, pPrev = NULL; if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE )) return ERROR_INVALID_HANDLE; // // If OpenPrinter failed, then this is a dummy handle. // We just need to free up the memory. // if ( pSpool->errOpenPrinter ) { // // invalidate the signature, but leave a recognizable value // pSpool->nSignature += 1 ; FreeNwSplMem( pSpool, sizeof( NWSPOOL) ); *phPrinter = NULL; return NO_ERROR; } pPrinter = pSpool->pPrinter; ASSERT( pPrinter ); // // Call EndDocPrinter if the user has not already done so // if ( pSpool->nStatus == SPOOL_STATUS_STARTDOC ) { (void) NwrEndDocPrinter( *phPrinter ); } else if ( pSpool->nStatus == SPOOL_STATUS_ADDJOB ) { (void) NwrScheduleJob( *phPrinter, pSpool->nJobNumber ); } if ( pSpool->hChangeEvent ) CloseHandle( pSpool->hChangeEvent ); pSpool->hChangeEvent = NULL; pSpool->nChangeFlags = 0; (VOID) NtClose( pSpool->hServer ); EnterCriticalSection( &NwPrintCritSec ); for ( pCur = pPrinter->pSpoolList; pCur; pPrev = pCur, pCur = pCur->pNextSpool ) { if ( pCur == pSpool ) { if ( pPrev ) pPrev->pNextSpool = pCur->pNextSpool; else pPrinter->pSpoolList = pCur->pNextSpool; break; } } ASSERT( pCur ); if ( pPrinter->pSpoolList == NULL ) { #if DBG IF_DEBUG(PRINT) { KdPrint(("*************DELETED PRINTER ENTRY: %ws\\%ws\n\n", pPrinter->pszServer, pPrinter->pszQueue )); } #endif NwRemovePrinterEntry( pPrinter ); } LeaveCriticalSection( &NwPrintCritSec ); // // invalidate the signature, but leave a recognizable value // pSpool->nSignature += 1 ; pSpool->pNextSpool = NULL; pSpool->pPrinter = NULL; // // Free all memory associated with the context handle // FreeNwSplMem( pSpool, sizeof( NWSPOOL) ); // // indicate to RPC we are done // *phPrinter = NULL; return NO_ERROR; } DWORD NwrGetPrinter( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN DWORD dwLevel, IN OUT LPBYTE pbPrinter, IN DWORD cbBuf, OUT 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: --*/ { PNWSPOOL pSpool = (PNWSPOOL) hPrinter; PNWPRINTER pPrinter; LPBYTE pbEnd = pbPrinter + cbBuf; BOOL fFitInBuffer; DWORD_PTR *pOffsets; if ( !pSpool || pSpool->nSignature != NW_SIGNATURE ) { return ERROR_INVALID_HANDLE; } else if ( pSpool->errOpenPrinter ) { return pSpool->errOpenPrinter; } else if ( ( dwLevel != 1 ) && ( dwLevel != 2 ) && ( dwLevel != 3 )) { return ERROR_INVALID_LEVEL; } if ( !pbPrinter ) { if ( cbBuf == 0 ) { // // Calculate size needed // pPrinter = pSpool->pPrinter; ASSERT( pPrinter ); if ( dwLevel == 1 ) { *pcbNeeded = sizeof( PRINTER_INFO_1W ) + ( wcslen( pPrinter->pszServer ) + wcslen( pPrinter->pszQueue ) + 2 ) * sizeof( WCHAR ); } else if ( dwLevel == 2 ) { *pcbNeeded = sizeof( PRINTER_INFO_2W ) + ( 2*wcslen( pPrinter->pszServer ) + 2*wcslen( pPrinter->pszQueue ) + 4 ) * sizeof( WCHAR ); } else // Level == 3 { PRINTER_INFO_3 *pPrinterInfo3 = (PRINTER_INFO_3 *) pbPrinter; *pcbNeeded = sizeof( PRINTER_INFO_3 ); } return ERROR_INSUFFICIENT_BUFFER; } else return ERROR_INVALID_PARAMETER; } pPrinter = pSpool->pPrinter; ASSERT( pPrinter ); if ( dwLevel == 1 ) { PRINTER_INFO_1W *pPrinterInfo1 = (PRINTER_INFO_1W *) pbPrinter; LPBYTE pbFixedEnd = pbPrinter + sizeof( PRINTER_INFO_1W ); // // Calculate size needed // *pcbNeeded = sizeof( PRINTER_INFO_1W ) + ( wcslen( pPrinter->pszServer ) + wcslen( pPrinter->pszQueue ) + 2 ) * sizeof( WCHAR ); if ( cbBuf < *pcbNeeded ) return ERROR_INSUFFICIENT_BUFFER; pOffsets = PrinterInfo1Offsets; // // Fill in the structure // pPrinterInfo1->Flags = PRINTER_ENUM_REMOTE | PRINTER_ENUM_NAME; pPrinterInfo1->pComment = NULL; fFitInBuffer = NwlibCopyStringToBuffer( pPrinter->pszServer, wcslen( pPrinter->pszServer ), (LPWSTR) pbFixedEnd, (LPWSTR *) &pbEnd, &pPrinterInfo1->pDescription ); ASSERT( fFitInBuffer ); fFitInBuffer = NwlibCopyStringToBuffer( pPrinter->pszQueue, wcslen( pPrinter->pszQueue ), (LPWSTR) pbFixedEnd, (LPWSTR *) &pbEnd, &pPrinterInfo1->pName ); ASSERT( fFitInBuffer ); } else if ( dwLevel == 2 ) { DWORD err; BYTE nQueueStatus; BYTE nNumJobs; PRINTER_INFO_2W *pPrinterInfo2 = (PRINTER_INFO_2W *) pbPrinter; LPBYTE pbFixedEnd = pbPrinter + sizeof( PRINTER_INFO_2W ); // // Check if the buffer is big enough to hold all the data // *pcbNeeded = sizeof( PRINTER_INFO_2W ) + ( 2*wcslen( pPrinter->pszServer ) + 2*wcslen( pPrinter->pszQueue ) + 4 ) * sizeof( WCHAR ); if ( cbBuf < *pcbNeeded ) return ERROR_INSUFFICIENT_BUFFER; pOffsets = PrinterInfo2Offsets; err = NwReadQueueCurrentStatus( pSpool->hServer, pPrinter->nQueueId, &nQueueStatus, &nNumJobs ); if ( err ) return err; pPrinterInfo2->Status = (nQueueStatus & 0x05)? PRINTER_STATUS_PAUSED : 0; pPrinterInfo2->cJobs = nNumJobs; fFitInBuffer = NwlibCopyStringToBuffer( pPrinter->pszServer, wcslen( pPrinter->pszServer ), (LPCWSTR) pbFixedEnd, (LPWSTR *) &pbEnd, &pPrinterInfo2->pServerName ); ASSERT( fFitInBuffer ); pbEnd -= ( wcslen( pPrinter->pszQueue) + 1 ) * sizeof( WCHAR ); wcscpy( (LPWSTR) pbEnd, pPrinter->pszQueue ); pbEnd -= ( wcslen( pPrinter->pszServer) + 1 ) * sizeof( WCHAR ); wcscpy( (LPWSTR) pbEnd, pPrinter->pszServer ); *(pbEnd + wcslen( pPrinter->pszServer )*sizeof(WCHAR))= L'\\'; pPrinterInfo2->pPrinterName = (LPWSTR) pbEnd; fFitInBuffer = NwlibCopyStringToBuffer( pPrinter->pszQueue, wcslen( pPrinter->pszQueue ), (LPCWSTR) pbFixedEnd, (LPWSTR *) &pbEnd, &pPrinterInfo2->pShareName ); ASSERT( fFitInBuffer ); pPrinterInfo2->pPortName = NULL; pPrinterInfo2->pDriverName = NULL; pPrinterInfo2->pComment = NULL; pPrinterInfo2->pLocation = NULL; pPrinterInfo2->pDevMode = NULL; pPrinterInfo2->pSepFile = NULL; pPrinterInfo2->pPrintProcessor = NULL; pPrinterInfo2->pDatatype = NULL; pPrinterInfo2->pParameters = NULL; pPrinterInfo2->pSecurityDescriptor = NULL; pPrinterInfo2->Attributes = PRINTER_ATTRIBUTE_QUEUED; pPrinterInfo2->Priority = 0; pPrinterInfo2->DefaultPriority = 0; pPrinterInfo2->StartTime = 0; pPrinterInfo2->UntilTime = 0; pPrinterInfo2->AveragePPM = 0; } else // Level == 3 { PRINTER_INFO_3 *pPrinterInfo3 = (PRINTER_INFO_3 *) pbPrinter; *pcbNeeded = sizeof( PRINTER_INFO_3 ); if ( cbBuf < *pcbNeeded ) return ERROR_INSUFFICIENT_BUFFER; pOffsets = PrinterInfo3Offsets; pPrinterInfo3->pSecurityDescriptor = NULL; } MarshallDownStructure( pbPrinter, pOffsets, pbPrinter ); return NO_ERROR; } DWORD NwrSetPrinter( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN DWORD dwCommand ) /*++ Routine Description: The routine sets information about the given printer. Arguments: hPrinter - Handle of the printer dwCommand - Specifies the new printer state Return Value: --*/ { PNWSPOOL pSpool = (PNWSPOOL) hPrinter; DWORD err = NO_ERROR; PNWPRINTER pPrinter; if ( !pSpool || pSpool->nSignature != NW_SIGNATURE ) { return ERROR_INVALID_HANDLE; } else if ( pSpool->errOpenPrinter ) { return pSpool->errOpenPrinter; } pPrinter = pSpool->pPrinter; ASSERT( pPrinter ); switch ( dwCommand ) { case PRINTER_CONTROL_PAUSE: case PRINTER_CONTROL_RESUME: { BYTE nQueueStatus = 0; BYTE nNumJobs; // // Get the original queue status so that we don't overwrite // some of the bits. // err = NwReadQueueCurrentStatus( pSpool->hServer, pPrinter->nQueueId, &nQueueStatus, &nNumJobs ); if ( !err ) { // // Clear the pause bits, and leave the rest alone. // nQueueStatus &= ~0x05; } if ( dwCommand == PRINTER_CONTROL_PAUSE ) { nQueueStatus |= 0x04; } err = NwSetQueueCurrentStatus( pSpool->hServer, pPrinter->nQueueId, nQueueStatus ); if ( !err ) NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_PRINTER ); break; } case PRINTER_CONTROL_PURGE: err = NwRemoveAllJobsFromQueue( pSpool->hServer, pPrinter->nQueueId ); if ( !err ) NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_PRINTER | PRINTER_CHANGE_DELETE_JOB ); break; default: // // dwCommand is 0 so that means // some properties of the printer has changed. // We will ignore the properties that // are being modified since most properties // are stored in the registry by spooler. // All we need to do is to signal WaitForPrinterChange to // return so that print manager will refresh its data. // ASSERT( dwCommand == 0 ); NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_PRINTER ); break; } return err; } DWORD NwrEnumPrinters( IN LPWSTR Reserved, IN LPWSTR pszName, IN OUT LPBYTE pbPrinter, IN DWORD cbBuf, OUT LPDWORD pcbNeeded, OUT LPDWORD pcReturned ) /*++ Routine Description: This routine enumerates the available providers, servers, printers depending on the given pszName. Arguments: Reserved - Unused pszName - The name of the container object 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: --*/ { PRINTER_INFO_1W *pPrinterInfo1 = (PRINTER_INFO_1W *) pbPrinter; *pcbNeeded = 0; *pcReturned = 0; if ( ( cbBuf != 0 ) && !pbPrinter ) { return ERROR_INVALID_PARAMETER; } if ( !pszName ) // Enumerate the provider name { BOOL fFitInBuffer; LPBYTE pbFixedEnd = pbPrinter + sizeof( PRINTER_INFO_1W ); LPBYTE pbEnd = pbPrinter + cbBuf; *pcbNeeded = sizeof( PRINTER_INFO_1W ) + ( 2 * wcslen( NwProviderName ) + + 2) * sizeof(WCHAR); if ( *pcbNeeded > cbBuf ) return ERROR_INSUFFICIENT_BUFFER; pPrinterInfo1->Flags = PRINTER_ENUM_ICON1 | PRINTER_ENUM_CONTAINER | PRINTER_ENUM_EXPAND; pPrinterInfo1->pComment = NULL; fFitInBuffer = NwlibCopyStringToBuffer( NwProviderName, wcslen( NwProviderName ), (LPWSTR) pbFixedEnd, (LPWSTR *) &pbEnd, &pPrinterInfo1->pDescription ); ASSERT( fFitInBuffer ); fFitInBuffer = NwlibCopyStringToBuffer( NwProviderName, wcslen( NwProviderName ), (LPWSTR) pbFixedEnd, (LPWSTR *) &pbEnd, &pPrinterInfo1->pName ); ASSERT( fFitInBuffer ); MarshallDownStructure( pbPrinter, PrinterInfo1Offsets, pbPrinter ); *pcReturned = 1; } else if ( pszName && *pszName ) { DWORD err; LPWSTR pszFullName; LPWSTR pszServer; NWWKSTA_CONTEXT_HANDLE handle; BYTE bTemp = 0; LPBYTE pbTempBuf = pbPrinter ? pbPrinter : &bTemp; if ( (pszFullName = LocalAlloc( 0, (wcslen( pszName ) + 1) * sizeof(WCHAR) ) ) == NULL ) return ERROR_NOT_ENOUGH_MEMORY; wcscpy( pszFullName, pszName ); pszServer = wcschr( pszFullName, L'!'); if ( pszServer ) *pszServer++ = 0; if ( lstrcmpiW( pszFullName, NwProviderName ) ) { LocalFree( pszFullName ); return ERROR_INVALID_NAME; } if ( !pszServer ) // Enumerate servers { LocalFree( pszFullName ); err = NwOpenEnumPrintServers( &handle ); if ( err != NO_ERROR ) { return err; } err = NwrEnum( handle, (DWORD_PTR) -1, pbTempBuf, cbBuf, pcbNeeded, pcReturned ); if ( err != NO_ERROR ) { NwrCloseEnum( &handle ); return err; } err = NwrCloseEnum( &handle ); if ( err != NO_ERROR ) { return err; } } else // Enumerate NDS sub-trees or print queues { LPWSTR tempStrPtr = pszServer; DWORD dwClassType = 0; if ( tempStrPtr[0] == L'\\' && tempStrPtr[1] == L'\\' && tempStrPtr[2] == L' ' ) tempStrPtr = &tempStrPtr[1]; err = NwrOpenEnumNdsSubTrees_Print( NULL, tempStrPtr, &dwClassType, &handle ); if ( err == ERROR_NETWORK_ACCESS_DENIED && dwClassType == CLASS_TYPE_NCP_SERVER ) { // An error code from the above NwOpenEnumNdsSubTrees could have // failed because the object was a server, which cannot be enumerated // with the NDS tree APIs. If so we try to get the print queues with the // regular NW APIs. tempStrPtr = NwGetUncObjectName( tempStrPtr ); err = NwOpenEnumPrintQueues( tempStrPtr, &handle ); if ( err != NO_ERROR ) { LocalFree( pszFullName ); return err; } } if ( err != NO_ERROR ) { // An error code from the above NwOpenEnumNdsSubTrees could have // failed because the object was not a part of an NDS tree. // So we try to get the print queues with the regular NW APIs. err = NwOpenEnumPrintQueues( tempStrPtr, &handle ); if ( err != NO_ERROR ) { LocalFree( pszFullName ); return err; } } // // Get rid of the allocated temp buffer that we've been using // indirectly through tempStrPtr and pszServer. // LocalFree( pszFullName ); err = NwrEnum( handle, 0xFFFFFFFF, pbTempBuf, cbBuf, pcbNeeded, pcReturned ); if ( err != NO_ERROR ) { NwrCloseEnum( &handle ); return err; } err = NwrCloseEnum( &handle ); if ( err != NO_ERROR ) { return err; } } } return NO_ERROR; } DWORD NwrStartDocPrinter( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN LPWSTR pszDocument, IN LPWSTR pszUser, IN DWORD PrintOptions //Multi-User Addition ) /*++ Routine Description: This routine informs the print spooler that a document is to be spooled for printing. Arguments: hPrinter - Handle of the printer pszDocument - Name of the document to be printed pszUser - Name of the user submitting the print job Return Value: --*/ { DWORD err; PNWSPOOL pSpool = (PNWSPOOL) hPrinter; if ( !pSpool || (pSpool->nSignature != NW_SIGNATURE) ) { err = ERROR_INVALID_HANDLE; } else if ( pSpool->errOpenPrinter ) { err = pSpool->errOpenPrinter; } else if ( pSpool->nStatus != 0 ) { err = ERROR_INVALID_PARAMETER; } else { // // Get pSpool->nJobNumber from CreateQueueJobAndFile // PNWPRINTER pPrinter = pSpool->pPrinter; WORD nJobNumber = 0; ASSERT( pPrinter ); err = NwCreateQueueJobAndFile( pSpool->hServer, pPrinter->nQueueId, pszDocument, pszUser, PrintOptions, //Multi-User addition pPrinter->pszQueue, &nJobNumber ); if ( !err ) { pSpool->nJobNumber = nJobNumber; pSpool->nStatus = SPOOL_STATUS_STARTDOC; NwSetPrinterChange( pSpool, PRINTER_CHANGE_ADD_JOB | PRINTER_CHANGE_SET_PRINTER ); } } return err; } DWORD NwrWritePrinter( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN LPBYTE pBuf, IN DWORD cbBuf, OUT 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: --*/ { DWORD err = NO_ERROR; PNWSPOOL pSpool = (PNWSPOOL) hPrinter; if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE)) { err = ERROR_INVALID_HANDLE; } else if ( pSpool->errOpenPrinter ) { err = pSpool->errOpenPrinter; } else if ( pSpool->nStatus != SPOOL_STATUS_STARTDOC ) { err = ERROR_INVALID_PARAMETER; } else { NTSTATUS ntstatus; IO_STATUS_BLOCK IoStatusBlock; PNWPRINTER pPrinter = pSpool->pPrinter; ASSERT( pPrinter ); ntstatus = NtWriteFile( pSpool->hServer, NULL, NULL, NULL, &IoStatusBlock, pBuf, cbBuf, NULL, NULL ); if ( NT_SUCCESS(ntstatus)) ntstatus = IoStatusBlock.Status; if ( NT_SUCCESS(ntstatus) ) { *pcbWritten = (DWORD) IoStatusBlock.Information; NwSetPrinterChange( pSpool, PRINTER_CHANGE_WRITE_JOB ); } else { KdPrint(("NWWORKSTATION: NtWriteFile failed 0x%08lx\n", ntstatus)); *pcbWritten = 0; err = RtlNtStatusToDosError( ntstatus ); } } return err; } DWORD NwrAbortPrinter( IN NWWKSTA_PRINTER_CONTEXT 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: --*/ { DWORD err; PNWSPOOL pSpool = (PNWSPOOL) hPrinter; if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE )) { err = ERROR_INVALID_HANDLE; } else if ( pSpool->errOpenPrinter ) { err = pSpool->errOpenPrinter; } else if ( pSpool->nStatus != SPOOL_STATUS_STARTDOC ) { err = ERROR_INVALID_PARAMETER; } else { PNWPRINTER pPrinter = pSpool->pPrinter; ASSERT( pPrinter ); err = NwRemoveJobFromQueue( pSpool->hServer, pPrinter->nQueueId, (WORD) pSpool->nJobNumber ); if ( !err ) { pSpool->nJobNumber = 0; pSpool->nStatus = SPOOL_STATUS_ABORT; NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_JOB ); } } return err; } DWORD NwrEndDocPrinter( IN NWWKSTA_PRINTER_CONTEXT hPrinter ) /*++ Routine Description: This routine ends the print job for the given printer. Arguments: hPrinter - Handle of the printer object Return Value: --*/ { DWORD err = NO_ERROR; PNWSPOOL pSpool = (PNWSPOOL) hPrinter; if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE )) { err = ERROR_INVALID_HANDLE; } else if ( pSpool->errOpenPrinter ) { err = pSpool->errOpenPrinter; } else if ( ( pSpool->nStatus != SPOOL_STATUS_STARTDOC ) && ( pSpool->nStatus != SPOOL_STATUS_ABORT ) ) { err = ERROR_INVALID_PARAMETER; } else { PNWPRINTER pPrinter = pSpool->pPrinter; ASSERT( pPrinter ); if ( pSpool->nStatus == SPOOL_STATUS_STARTDOC ) { err = NwCloseFileAndStartQueueJob( pSpool->hServer, pPrinter->nQueueId, (WORD) pSpool->nJobNumber ); if ( !err ) NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_JOB ); } if ( !err ) { pSpool->nJobNumber = 0; pSpool->nStatus = 0; } } return err; } DWORD NwrGetJob( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN DWORD dwJobId, IN DWORD dwLevel, IN OUT LPBYTE pbJob, IN DWORD cbBuf, OUT LPDWORD pcbNeeded ) /*++ Routine Description: Arguments: hPrinter - Handle of the printer dwJobId - dwLevel - pbJob - cbBuf - pcbNeeded - Return Value: --*/ { DWORD err; PNWSPOOL pSpool = (PNWSPOOL) hPrinter; if ( !pSpool || pSpool->nSignature != NW_SIGNATURE ) { err = ERROR_INVALID_HANDLE; } // allow NULL for bpJob if cbBuf is 0. // Relies on NwGetQueueJobInfo to properly handle NULL pointer in request to fill pcbNeeded else if ( (cbBuf != 0) && ( !pbJob ) ) { err = ERROR_INVALID_PARAMETER; } else if ( pSpool->errOpenPrinter ) { err = pSpool->errOpenPrinter; } else if (( dwLevel != 1 ) && ( dwLevel != 2 )) { err = ERROR_INVALID_LEVEL; } else { DWORD nPrinterLen; LPWSTR pszPrinter; LPBYTE FixedPortion = pbJob; LPWSTR EndOfVariableData = (LPWSTR) (pbJob + cbBuf); PNWPRINTER pPrinter = pSpool->pPrinter; ASSERT( pPrinter ); pszPrinter = AllocNwSplMem( LMEM_ZEROINIT, nPrinterLen = ( wcslen( pPrinter->pszServer) + wcslen( pPrinter->pszQueue) + 2) * sizeof(WCHAR)); if ( pszPrinter == NULL ) return ERROR_NOT_ENOUGH_MEMORY; wcscpy( pszPrinter, pPrinter->pszServer ); wcscat( pszPrinter, L"\\" ); wcscat( pszPrinter, pPrinter->pszQueue ); *pcbNeeded = 0; err = NwGetQueueJobInfo( pSpool->hServer, pPrinter->nQueueId, (WORD) dwJobId, pszPrinter, dwLevel, &FixedPortion, &EndOfVariableData, pcbNeeded ); FreeNwSplMem( pszPrinter, nPrinterLen ); if ( !err ) { switch( dwLevel ) { case 1: MarshallDownStructure( pbJob, JobInfo1Offsets, pbJob ); break; case 2: MarshallDownStructure( pbJob, JobInfo2Offsets, pbJob ); break; default: ASSERT( FALSE ); break; } } } return err; } DWORD NwrEnumJobs( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN DWORD dwFirstJob, IN DWORD dwNoJobs, IN DWORD dwLevel, IN OUT LPBYTE pbJob, IN DWORD cbBuf, OUT LPDWORD pcbNeeded, OUT LPDWORD pcReturned ) /*++ Routine Description: Arguments: hPrinter - Handle of the printer dwFirstJob - dwNoJobs - dwLevel - pbJob - cbBuf - pcbNeeded - pcReturned - Return Value: --*/ { DWORD err; PNWSPOOL pSpool = (PNWSPOOL) hPrinter; if ( !pSpool || pSpool->nSignature != NW_SIGNATURE ) { err = ERROR_INVALID_HANDLE; } // allow NULL for bpJob if cbBuf is 0. // Relies on NwGetQueueJobInfo to properly handle NULL pointer in request to fill pcbNeeded else if ( (cbBuf != 0) && ( !pbJob ) ) { err = ERROR_INVALID_PARAMETER; } else if ( pSpool->errOpenPrinter ) { err = pSpool->errOpenPrinter; } else if ( ( dwLevel != 1 ) && ( dwLevel != 2 ) ) { err = ERROR_INVALID_LEVEL; } else { PNWPRINTER pPrinter = pSpool->pPrinter; LPWSTR pszPrinter; DWORD nPrinterLen; ASSERT( pPrinter ); pszPrinter = AllocNwSplMem( LMEM_ZEROINIT, nPrinterLen = ( wcslen( pPrinter->pszServer ) + wcslen( pPrinter->pszQueue) + 2) * sizeof(WCHAR)); if ( pszPrinter == NULL ) return ERROR_NOT_ENOUGH_MEMORY; wcscpy( pszPrinter, pPrinter->pszServer ); wcscat( pszPrinter, L"\\" ); wcscat( pszPrinter, pPrinter->pszQueue ); err = NwGetQueueJobs( pSpool->hServer, pPrinter->nQueueId, pszPrinter, dwFirstJob, dwNoJobs, dwLevel, pbJob, cbBuf, pcbNeeded, pcReturned ); FreeNwSplMem( pszPrinter, nPrinterLen ); if ( !err ) { DWORD_PTR *pOffsets; DWORD cbStruct; DWORD cReturned = *pcReturned; LPBYTE pbBuffer = pbJob; switch( dwLevel ) { case 1: pOffsets = JobInfo1Offsets; cbStruct = sizeof( JOB_INFO_1W ); break; case 2: pOffsets = JobInfo2Offsets; cbStruct = sizeof( JOB_INFO_2W ); break; default: ASSERT( FALSE ); break; } while ( cReturned-- ) { MarshallDownStructure( pbBuffer, pOffsets, pbJob ); pbBuffer += cbStruct; } } } return err; } DWORD NwrSetJob( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN DWORD dwJobId, IN DWORD dwLevel, IN PNW_JOB_INFO pNwJobInfo, IN DWORD dwCommand ) /*++ Routine Description: Arguments: hPrinter - Handle of the printer dwJobId - dwLevel - pNwJobInfo- dwCommand - Return Value: --*/ { DWORD err = NO_ERROR; PNWSPOOL pSpool = (PNWSPOOL) hPrinter; PNWPRINTER pPrinter; if ( !pSpool || pSpool->nSignature != NW_SIGNATURE ) { err = ERROR_INVALID_HANDLE; } else if ( ( dwLevel != 0 ) && ( !pNwJobInfo ) ) { err = ERROR_INVALID_PARAMETER; } else if ( pSpool->errOpenPrinter ) { err = pSpool->errOpenPrinter; } else if ( ( dwLevel != 0 ) && ( dwLevel != 1 ) && ( dwLevel != 2 ) ) { err = ERROR_INVALID_LEVEL; } if ( err ) return err; pPrinter = pSpool->pPrinter; ASSERT( pPrinter ); if ( ( dwCommand == JOB_CONTROL_CANCEL ) || ( dwCommand == JOB_CONTROL_DELETE ) ) { err = NwRemoveJobFromQueue( pSpool->hServer, pPrinter->nQueueId, (WORD) dwJobId ); if ( !err ) NwSetPrinterChange( pSpool, PRINTER_CHANGE_DELETE_JOB | PRINTER_CHANGE_SET_PRINTER ); // Since the job is removed, we don't need to change other // information about it. } else { if ( dwLevel != 0 ) { if ( pNwJobInfo->nPosition != JOB_POSITION_UNSPECIFIED ) { err = NwChangeQueueJobPosition( pSpool->hServer, pPrinter->nQueueId, (WORD) dwJobId, (BYTE) pNwJobInfo->nPosition ); } } if ( ( !err ) && ( dwCommand == JOB_CONTROL_RESTART )) { err = ERROR_NOT_SUPPORTED; } else if ( !err ) { err = NwChangeQueueJobEntry( pSpool->hServer, pPrinter->nQueueId, (WORD) dwJobId, dwCommand, pNwJobInfo ); } if ( !err ) NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_JOB ); } return err; } DWORD NwrAddJob( IN NWWKSTA_PRINTER_CONTEXT hPrinter, OUT LPADDJOB_INFO_1W pAddInfo1, IN DWORD cbBuf, OUT LPDWORD pcbNeeded ) /*++ Routine Description: Arguments: hPrinter - Handle of the printer. pAddInfo1 - Output buffer to hold ADDJOB_INFO_1W structure. cbBuf - Output buffer size in bytes. pcbNeeded - Required output buffer size in bytes. Return Value: --*/ { PNWSPOOL pSpool = (PNWSPOOL) hPrinter; PNWPRINTER pPrinter; if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE )) { return ERROR_INVALID_HANDLE; } if ( pSpool->errOpenPrinter ) { return pSpool->errOpenPrinter; } if ( pSpool->nStatus != 0 ) { return ERROR_INVALID_PARAMETER; } pPrinter = pSpool->pPrinter; ASSERT( pPrinter ); *pcbNeeded = sizeof(ADDJOB_INFO_1W) + (wcslen(pPrinter->pszServer) + wcslen(pPrinter->pszQueue) + 2) * sizeof(WCHAR); if (cbBuf < *pcbNeeded) { return ERROR_INSUFFICIENT_BUFFER; } // // Write UNC path name into the output buffer. // // dfergus 19 Apr 2001 - 348006 // DWORD cast pAddInfo1->Path = (LPWSTR) ((DWORD) pAddInfo1 + sizeof(ADDJOB_INFO_1W)); // wcscpy(pAddInfo1->Path, pPrinter->pszServer); wcscat(pAddInfo1->Path, L"\\" ); wcscat(pAddInfo1->Path, pPrinter->pszQueue); // // Return special job id value which the client (winspool.drv) looks // for and does an FSCTL call to our redirector to get the real // job id. We cannot return a real job id at this point because // the CreateQueueJobAndFile NCP is not issue until the client opens // the UNC name we return in this API. // pAddInfo1->JobId = (DWORD) -1; // // Save context information // pSpool->nJobNumber = pAddInfo1->JobId; pSpool->nStatus = SPOOL_STATUS_ADDJOB; #if DBG IF_DEBUG(PRINT) { KdPrint(("NWWORKSTATION: NwrAddJob Path=%ws, JobId=%lu, BytesNeeded=%lu\n", pAddInfo1->Path, pAddInfo1->JobId, *pcbNeeded)); } #endif NwSetPrinterChange( pSpool, PRINTER_CHANGE_ADD_JOB | PRINTER_CHANGE_SET_PRINTER ); return NO_ERROR; } DWORD NwrScheduleJob( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN DWORD dwJobId ) /*++ Routine Description: Arguments: hPrinter - Handle of the printer dwJobId - Job identification number Return Value: --*/ { PNWSPOOL pSpool = (PNWSPOOL) hPrinter; PNWPRINTER pPrinter; if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE )) { return ERROR_INVALID_HANDLE; } if ( pSpool->errOpenPrinter ) { return pSpool->errOpenPrinter; } if (pSpool->nStatus != SPOOL_STATUS_ADDJOB) { return ERROR_INVALID_PARAMETER; } pPrinter = pSpool->pPrinter; ASSERT( pPrinter ); pSpool->nJobNumber = 0; pSpool->nStatus = 0; NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_JOB ); return NO_ERROR; } DWORD NwrWaitForPrinterChange( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN OUT LPDWORD pdwFlags ) /*++ Routine Description: Arguments: hPrinter - Handle of the printer pdwFlags - Return Value: --*/ { PNWSPOOL pSpool = (PNWSPOOL) hPrinter; HANDLE hChangeEvent = NULL; DWORD nRetVal; HANDLE ahWaitEvents[2]; DWORD err = NO_ERROR; if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE )) { return ERROR_INVALID_HANDLE; } else if ( pSpool->errOpenPrinter ) { return pSpool->errOpenPrinter; } else if ( pSpool->hChangeEvent ) { return ERROR_ALREADY_WAITING; } else if ( !(*pdwFlags & PRINTER_CHANGE_VALID )) { return ERROR_INVALID_PARAMETER; } if ( pSpool->nChangeFlags & *pdwFlags ) { // // There is a change since we last called // *pdwFlags &= pSpool->nChangeFlags; EnterCriticalSection( &NwPrintCritSec ); pSpool->nChangeFlags = 0; LeaveCriticalSection( &NwPrintCritSec ); return NO_ERROR; } hChangeEvent = CreateEvent( NULL, FALSE, // automatic reset FALSE, // initial state not signalled NULL ); if ( !hChangeEvent ) { KdPrint(("WaitForPrinterChange: CreateEvent failed with error %d\n", GetLastError() )); return GetLastError(); } pSpool->nWaitFlags = *pdwFlags; EnterCriticalSection( &NwPrintCritSec ); pSpool->hChangeEvent = hChangeEvent; pSpool->nChangeFlags = 0; LeaveCriticalSection( &NwPrintCritSec ); ahWaitEvents[0] = pSpool->hChangeEvent; ahWaitEvents[1] = NwDoneEvent; nRetVal = WaitForMultipleObjects( 2, // Two events to wait for ahWaitEvents, FALSE, // Wait for one to signal NwTimeOutValue ); switch ( nRetVal ) { case WAIT_FAILED: err = GetLastError(); break; case WAIT_TIMEOUT: case WAIT_OBJECT_0 + 1: // treats service stopping as timeout *pdwFlags |= PRINTER_CHANGE_TIMEOUT; break; case WAIT_OBJECT_0: *pdwFlags &= pSpool->nChangeFlags; break; default: KdPrint(("WaitForPrinterChange: WaitForMultipleObjects returned with %d\n", nRetVal )); *pdwFlags |= PRINTER_CHANGE_TIMEOUT; break; } if ( ( !err ) && ( nRetVal != WAIT_OBJECT_0 + 1 ) ) { pSpool->nWaitFlags = 0; EnterCriticalSection( &NwPrintCritSec ); pSpool->nChangeFlags = 0; pSpool->hChangeEvent = NULL; LeaveCriticalSection( &NwPrintCritSec ); } if ( !CloseHandle( hChangeEvent ) ) { KdPrint(("WaitForPrinterChange: CloseHandle failed with error %d\n", GetLastError())); } return err; } typedef DWORD (*PRINTPROC)(HWND, HINSTANCE, LPCTSTR, UINT); DWORD NwrAddPrinterConnection( IN LPWSTR Reserved, IN LPWSTR pszPrinterName, IN LPWSTR pszDriverName ) /*++ Routine Description: Implements workaround to allow CSNW to support Point and Print If PnP is enabled in registry, NwrAddPrinterConnection is called with driver name read from NDS Queue object Description attribute in format: "PnP Driver:Driver Friendly Name[@\\server\share\driver.inf]". If driver inf file is specified it must be a Logo'd PnP driver, and must not invoke UI. If not specified, driver name must be from %windir%\inf\ntprint.inf. Thie routine purposely runs in the system context, not impersonating the user in order to load the printer driver. To maintain administrative control the feature requires a registry setting to enable PnP configuration, and another key can be set to restrict PnP configuration to using only drivers listed in ntprint.inf. \\HKLM\System\CurrentControlSet\Control\Print\Providers\NetWare or Conpatible Network DWORD Value: EnableUserAddPrinter = 1 enables PnP loading of printer drivers ( Default: 0 ) DWORD Value: RestrictToInboxDrivers = 1 forces loading from ntprint.inf only ( Default: 0 ) Arguments: Reserved - RPC Handle not used pszPrinterName - NDS Queue object pszDriverName - Friendly name of printer driver, with optional driver location Return Value: --*/ { DWORD err = 0; PRINTER_INFO_2W * pPrinterInfo = NULL; HANDLE hPrinter = NULL; PSECURITY_DESCRIPTOR pSecDesc = NULL; PACL pDacl = NULL; LPTSTR pszProcessor = L"winprint"; NWWKSTA_PRINTER_CONTEXT phPrinter = NULL; LPBYTE pDest = NULL; DWORD dwBytesNeeded = 0; BOOL bRet = FALSE; LPWSTR pszInfPath = NULL; PSID pEveryoneSID = NULL; EXPLICIT_ACCESS ea[1]; SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; WCHAR szPrintInf[MAX_PATH]; if (!pszPrinterName || (*pszPrinterName == 0)) return ERROR_INVALID_PARAMETER; if (!pszDriverName || (*pszDriverName == 0)) return ERROR_INVALID_PARAMETER; pszInfPath = wcschr(pszDriverName, L'@'); if (pszInfPath) { *pszInfPath = 0; pszInfPath++; } else { szPrintInf[0] = 0; err = GetSystemWindowsDirectory(szPrintInf, MAX_PATH); lstrcat(szPrintInf, L"\\inf\\ntprint.inf"); pszInfPath = szPrintInf; } #if DBG IF_DEBUG(PRINT) { KdPrint(("NWWKS:AddPrinterConnection - Printer=%ws, driver=%ws from %ws\n", pszPrinterName, pszDriverName, szPrintInf)); } #endif // Create a well-known SID for the Everyone group. if(! AllocateAndInitializeSid( &SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSID) ) { goto Cleanup; } // Initialize an EXPLICIT_ACCESS structure for an ACE. // The ACE will allow Everyone read access to the key. ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS)); ea[0].grfAccessPermissions = PRINTER_ALL_ACCESS; ea[0].grfAccessMode = SET_ACCESS; ea[0].grfInheritance= OBJECT_INHERIT_ACE; ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; ea[0].Trustee.ptstrName = (LPTSTR) pEveryoneSID; // Create a new ACL that contains the new ACE. err = SetEntriesInAcl(1, ea, NULL, &pDacl); if (ERROR_SUCCESS != err) { goto Cleanup; } // Initialize a security descriptor. pSecDesc = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); if (pSecDesc == NULL) { goto Cleanup; } if (!InitializeSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION)) { goto Cleanup; } // Add the ACL to the security descriptor. if (!SetSecurityDescriptorDacl(pSecDesc, TRUE, // fDaclPresent flag pDacl, FALSE)) // not a default DACL { goto Cleanup; } dwBytesNeeded = sizeof(PRINTER_INFO_2); pPrinterInfo = (PRINTER_INFO_2 *) LocalAlloc(LPTR, dwBytesNeeded); if (pPrinterInfo) { pPrinterInfo->pPrinterName = pszPrinterName; pPrinterInfo->pPortName = pPrinterInfo->pPrinterName; pPrinterInfo->pDriverName = pszDriverName; pPrinterInfo->pPrintProcessor = pszProcessor; pPrinterInfo->Attributes = PRINTER_ATTRIBUTE_NETWORK | PRINTER_ATTRIBUTE_LOCAL; pPrinterInfo->pServerName = NULL; pPrinterInfo->pShareName = NULL; pPrinterInfo->Status = 0; pPrinterInfo->cJobs = 0; if (IsValidSecurityDescriptor(pSecDesc)) { pPrinterInfo->pSecurityDescriptor = pSecDesc; } hPrinter = AddPrinter(NULL, 2, (LPBYTE) pPrinterInfo); if (hPrinter == NULL) { if ( GetLastError() == ERROR_UNKNOWN_PRINTER_DRIVER ) { WCHAR szCmdLine[1024]; _snwprintf(szCmdLine, sizeof(szCmdLine) / sizeof(WCHAR), L"/ia /q /m \"%.200ws\" /h \"Intel\" /v \"Windows 2000 or XP\" /f \"%.768ws\"", pPrinterInfo->pDriverName, pszInfPath); #if DBG IF_DEBUG(PRINT) { KdPrint(("AddPrinterConnection - attempting to load driver from cab with command\n %ws\n", szCmdLine)); } #endif { HINSTANCE hPrintDll = LoadLibrary(L"printui.dll"); PRINTPROC pPrintFunc = NULL; if (hPrintDll) { pPrintFunc = (PRINTPROC) GetProcAddress((HMODULE)hPrintDll, "PrintUIEntryW"); if (pPrintFunc) { HINSTANCE hInst = (HINSTANCE) GetModuleHandle(NULL); HWND hParentWnd = GetDesktopWindow(); DWORD nRet = 0; nRet = (pPrintFunc) (hParentWnd, hInst, szCmdLine, TRUE); hPrinter = AddPrinter(NULL, 2, (LPBYTE) pPrinterInfo); } FreeLibrary(hPrintDll); } } } } #if DBG IF_DEBUG(PRINT) { KdPrint(("AddPrinter returned 0x%8.8X\n", hPrinter )); } #endif } Cleanup: if (pEveryoneSID) FreeSid(pEveryoneSID); if (pDacl) LocalFree(pDacl); if (pSecDesc) LocalFree(pSecDesc); if (hPrinter) ClosePrinter(hPrinter); if (phPrinter) ClosePrinter(phPrinter); if (pPrinterInfo) LocalFree(pPrinterInfo); return (hPrinter) ? ERROR_SUCCESS : ERROR_ACCESS_DENIED; } VOID NwSetPrinterChange( PNWSPOOL pSpool, DWORD nFlags ) { PNWPRINTER pPrinter = pSpool->pPrinter; PNWSPOOL pCurSpool = pSpool; EnterCriticalSection( &NwPrintCritSec ); do { if ( pCurSpool->nWaitFlags & nFlags ) { pCurSpool->nChangeFlags |= nFlags; if ( pCurSpool->hChangeEvent ) { SetEvent( pCurSpool->hChangeEvent ); pCurSpool->hChangeEvent = NULL; } } pCurSpool = pCurSpool->pNextSpool; if ( pCurSpool == NULL ) pCurSpool = pPrinter->pSpoolList; } while ( pCurSpool && (pCurSpool != pSpool) ); LeaveCriticalSection( &NwPrintCritSec ); } PNWPRINTER NwFindPrinterEntry( IN LPWSTR pszServer, IN LPWSTR pszQueue ) { PNWPRINTER pPrinter = NULL; // // Check to see if we already have the given printer in our printer // link list. If yes, return the printer. // for ( pPrinter = NwPrinterList; pPrinter; pPrinter = pPrinter->pNextPrinter) { if ( ( lstrcmpiW( pPrinter->pszServer, pszServer ) == 0 ) && ( lstrcmpiW( pPrinter->pszQueue, pszQueue ) == 0 ) ) { return pPrinter; } } return NULL; } DWORD NwCreatePrinterEntry( IN LPWSTR pszServer, IN LPWSTR pszQueue, OUT PNWPRINTER *ppPrinter, OUT PHANDLE phServer ) { DWORD err = NO_ERROR; DWORD nQueueId = 0; HANDLE TreeHandle = NULL; UNICODE_STRING TreeName; PNWPRINTER pNwPrinter = NULL; BOOL fCreatedNWConnection = FALSE; LPWSTR lpRemoteName = NULL; DWORD dwBufSize = ( wcslen(pszServer) + wcslen(pszQueue) + 2 ) * sizeof(WCHAR); lpRemoteName = (LPWSTR) AllocNwSplMem( LMEM_ZEROINIT, dwBufSize ); if ( lpRemoteName == NULL ) return ERROR_NOT_ENOUGH_MEMORY; wcscpy( lpRemoteName, pszServer ); wcscat( lpRemoteName, L"\\" ); wcscat( lpRemoteName, pszQueue ); *ppPrinter = NULL; *phServer = NULL; // // See if we already know about this print queue. // pNwPrinter = NwFindPrinterEntry( pszServer, pszQueue ); /* Changing to get queue status to verify access to queue instead if ( pNwPrinter == NULL ) { // We don't know about this NetWare print queue. We need to see if // we are authorized to use this queue. If so, then go ahead // and continue to open printer. Otherwise, fail with not // authorized error code. err = NwCreateConnection( NULL, lpRemoteName, RESOURCETYPE_PRINT, NULL, NULL ); if ( err != NO_ERROR ) { if ( ( err == ERROR_INVALID_PASSWORD ) || ( err == ERROR_ACCESS_DENIED ) || ( err == ERROR_NO_SUCH_USER ) ) { err = ERROR_ACCESS_DENIED; } FreeNwSplMem( lpRemoteName, dwBufSize ); return err; } fCreatedNWConnection = TRUE; } */ // // See if pszServer is really a NDS tree name, if so call // NwNdsGetQueueInformation to get the QueueId and possible referred // server for which we open handle. // RtlInitUnicodeString( &TreeName, pszServer + 2 ); err = NwNdsOpenTreeHandle( &TreeName, &TreeHandle ); if ( err == NO_ERROR ) { NTSTATUS ntstatus; WCHAR szRefServer[NDS_MAX_NAME_CHARS]; UNICODE_STRING ObjectName; UNICODE_STRING QueuePath; ObjectName.Buffer = szRefServer; ObjectName.MaximumLength = NDS_MAX_NAME_CHARS; ObjectName.Length = 0; RtlInitUnicodeString( &QueuePath, pszQueue ); ntstatus = NwNdsGetQueueInformation( TreeHandle, &QueuePath, &ObjectName, &nQueueId ); if ( TreeHandle ) { CloseHandle( TreeHandle ); TreeHandle = NULL; } if ( ntstatus ) { err = RtlNtStatusToDosError( ntstatus ); goto ErrorExit; } // // If we got a referred server, it's name would look like: // "CN=SERVER.OU=DEV.O=MICROSOFT" . . . Convert it to "C\\SERVER" // if ( ObjectName.Length > 0 ) { WORD i; LPWSTR EndOfServerName = NULL; // // First convert the referred server name to // "C\\SERVER.OU=DEV.O=MICROSOFT" // szRefServer[1] = L'\\'; szRefServer[2] = L'\\'; // // Put a NULL terminator at the first '.' // EndOfServerName = wcschr( szRefServer + 3, L'.' ); if (EndOfServerName) *EndOfServerName = L'\0'; // // pszServer now equals the referred server "C\\SERVER" // // // Get the handle of the referred server skipping the 'C' character. // err = NwAttachToNetwareServer( szRefServer + 1, phServer); } } else // Not an NDS tree, so get handle of server. { err = NwAttachToNetwareServer( pszServer, phServer); if ( err == NO_ERROR ) { if ( err = NwGetQueueId( *phServer, pszQueue, &nQueueId)) err = ERROR_INVALID_NAME; } } if ( ( err == ERROR_INVALID_PASSWORD ) || ( err == ERROR_ACCESS_DENIED ) || ( err == ERROR_NO_SUCH_USER ) ) { err = ERROR_ACCESS_DENIED; goto ErrorExit; } else if ( err != NO_ERROR ) { err = ERROR_INVALID_NAME; goto ErrorExit; } // // Test to see if there already was a entry for this print queue. If so, // we can now return with NO_ERROR since pNwPrinter and phServer are // now set. // if ( pNwPrinter ) { if ( lpRemoteName ) { FreeNwSplMem( lpRemoteName, dwBufSize ); } *ppPrinter = pNwPrinter; return NO_ERROR; } // // The printer entry was not found in our list of printers in the // call to NwFindPrinterEntry. So, we must create one. // // First, verify access rights else { BYTE nQueueStatus; BYTE nJobCount; err = NwReadQueueCurrentStatus(*phServer, nQueueId, &nQueueStatus, &nJobCount); if(err != NO_ERROR) { err = NwCreateConnection( NULL, lpRemoteName, RESOURCETYPE_PRINT, NULL, NULL ); } if ( ( err == ERROR_INVALID_PASSWORD ) || ( err == ERROR_ACCESS_DENIED ) || ( err == ERROR_NO_SUCH_USER ) ) { err = ERROR_ACCESS_DENIED; goto ErrorExit; } else if ( err != NO_ERROR ) { err = ERROR_INVALID_NAME; goto ErrorExit; } } if ( *ppPrinter = AllocNwSplMem( LMEM_ZEROINIT, sizeof(NWPRINTER) )) { if ( !( (*ppPrinter)->pszServer = AllocNwSplStr( pszServer )) ) { err = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } else if ( !( (*ppPrinter)->pszQueue = AllocNwSplStr( pszQueue ))) { err = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } if ( fCreatedNWConnection ) { if ( !( (*ppPrinter)->pszUncConnection = AllocNwSplStr( lpRemoteName )) ) { err = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } FreeNwSplMem( lpRemoteName, dwBufSize ); lpRemoteName = NULL; } else { (*ppPrinter)->pszUncConnection = NULL; } #if DBG IF_DEBUG(PRINT) { KdPrint(("*************CREATED PRINTER ENTRY: %ws\\%ws\n\n", (*ppPrinter)->pszServer, (*ppPrinter)->pszQueue )); } #endif (*ppPrinter)->nQueueId = nQueueId; (*ppPrinter)->pSpoolList = NULL; (*ppPrinter)->pNextPrinter = NwPrinterList; NwPrinterList = *ppPrinter; err = NO_ERROR; } else { err = ERROR_NOT_ENOUGH_MEMORY; } if ( err == NO_ERROR ) return err; ErrorExit: if ( *phServer ) { (VOID) NtClose( *phServer ); *phServer = NULL; } if ( *ppPrinter ) { if ( (*ppPrinter)->pszServer ) { FreeNwSplStr( (*ppPrinter)->pszServer ); } if ( (*ppPrinter)->pszQueue ) { FreeNwSplStr( (*ppPrinter)->pszQueue ); } if ( (*ppPrinter)->pszUncConnection ) { (void) NwrDeleteConnection( NULL, (*ppPrinter)->pszUncConnection, FALSE ); FreeNwSplStr( (*ppPrinter)->pszUncConnection ); } FreeNwSplMem( *ppPrinter, sizeof( NWPRINTER)); *ppPrinter = NULL; } if ( lpRemoteName ) { FreeNwSplMem( lpRemoteName, dwBufSize ); } return err; } VOID NwRemovePrinterEntry( IN PNWPRINTER pPrinter ) { PNWPRINTER pCur, pPrev = NULL; ASSERT( pPrinter->pSpoolList == NULL ); pPrinter->pSpoolList = NULL; for ( pCur = NwPrinterList; pCur; pPrev = pCur, pCur = pCur->pNextPrinter ) { if ( pCur == pPrinter ) { if ( pPrev ) pPrev->pNextPrinter = pCur->pNextPrinter; else NwPrinterList = pCur->pNextPrinter; break; } } ASSERT( pCur ); pPrinter->pNextPrinter = NULL; FreeNwSplStr( pPrinter->pszServer ); FreeNwSplStr( pPrinter->pszQueue ); if ( pPrinter->pszUncConnection ) { (void) NwrDeleteConnection( NULL, pPrinter->pszUncConnection, FALSE ); FreeNwSplStr( pPrinter->pszUncConnection ); } FreeNwSplMem( pPrinter, sizeof( NWPRINTER)); } VOID NWWKSTA_PRINTER_CONTEXT_rundown( IN NWWKSTA_PRINTER_CONTEXT hPrinter ) /*++ Routine Description: This function is called by RPC when a client terminates with an opened handle. This allows us to clean up and deallocate any context data associated with the handle. Arguments: hPrinter - Supplies the opened handle Return Value: None. --*/ { (void) NwrClosePrinter(&hPrinter); } LPWSTR NwGetUncObjectName( IN LPWSTR ContainerName ) { WORD length = 2; WORD totalLength = (WORD) wcslen( ContainerName ); if ( totalLength < 2 ) return 0; while ( length < totalLength ) { if ( ContainerName[length] == L'.' ) ContainerName[length] = L'\0'; length++; } length = 2; while ( length < totalLength && ContainerName[length] != L'\\' ) { length++; } ContainerName[length + 2] = L'\\'; ContainerName[length + 3] = L'\\'; return (ContainerName + length + 2); }