/*++ 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 //------------------------------------------------------------------ // // 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 ); DWORD 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; // Use the minimum timeout value if the // value set in the registry is too small. if ( NwTimeOutValue >= 0 && 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)); } } 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; UNREFERENCED_PARAMETER( Reserved ); if ( (pszServer = AllocNwSplStr( pszPrinterName )) == NULL ) return ERROR_NOT_ENOUGH_MEMORY; CharUpperW( pszServer ); // convert in place // // ValidatePrinterName // if ( ( !ValidateUNCName( pszPrinterName ) ) || ( (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 *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; } 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 ( !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; WCHAR szFullName[MAX_PATH]; LPWSTR pszServer; NWWKSTA_CONTEXT_HANDLE handle; wcscpy( szFullName, pszName ); pszServer = wcschr( szFullName, L'!'); if ( pszServer ) *pszServer++ = 0; if ( lstrcmpiW( szFullName, NwProviderName ) ) return ERROR_INVALID_NAME; if ( !pszServer ) // Enumerate servers { err = NwOpenEnumPrintServers( &handle ); if ( err != NO_ERROR ) { return err; } err = NwrEnum( handle, 0xFFFFFFFF, pbPrinter, 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. // BUGBUG - Temporary hack to browse past CN= in an NDS tree. tempStrPtr = (LPWSTR) NwGetUncObjectName( tempStrPtr ); err = NwOpenEnumPrintQueues( tempStrPtr, &handle ); if ( err != NO_ERROR ) { 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 ) { return err; } } err = NwrEnum( handle, 0xFFFFFFFF, pbPrinter, 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 fGateway ) /*++ 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 fGateway - TRUE if it is gateway printing 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; ASSERT( pPrinter ); err = NwCreateQueueJobAndFile( pSpool->hServer, pPrinter->nQueueId, pszDocument, pszUser, fGateway, 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 = 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; } 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) ((DWORD) 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; } 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 *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 ( 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 ) { 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. // 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 )) // BUGBUG: do we need this? { 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; } 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 ); 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 ) ) { // BUGBUG err = ERROR_INVALID_PASSWORD; err = ERROR_ACCESS_DENIED; } FreeNwSplMem( lpRemoteName, dwBufSize ); if ( TreeHandle ) CloseHandle( TreeHandle ); 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 ) ) { // BUGBUG err = ERROR_INVALID_PASSWORD; 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. // 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); } DWORD NwGetUncObjectName( IN LPWSTR ContainerName ) { WORD length = 2; WORD totalLength = 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 (DWORD) (ContainerName + length + 2); }