Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

407 lines
11 KiB

/*++
Copyright (c) 1990-2003 Microsoft Corporation
All rights reserved
Module Name:
winspool.c
Abstract:
Implements the spooler supported apis for printing.
// @@BEGIN_DDKSPLIT
Author:
Environment:
User Mode -Win32
Revision History:
// @@END_DDKSPLIT
--*/
#include "precomp.h"
#pragma hdrstop
WCHAR szNULL[] = L"";
WCHAR szLcmDeviceNameHeader[] = L"\\Device\\NamedPipe\\Spooler\\";
WCHAR szWindows[] = L"windows";
WCHAR szINIKey_TransmissionRetryTimeout[] = L"TransmissionRetryTimeout";
//
// Timeouts for serial printing
//
#define WRITE_TOTAL_TIMEOUT 3000 // 3 seconds
#define READ_TOTAL_TIMEOUT 5000 // 5 seconds
#define READ_INTERVAL_TIMEOUT 200 // 0.2 second
BOOL
DeletePortNode(
PINILOCALMON pIniLocalMon,
PINIPORT pIniPort
)
{
PINIPORT pPort, pPrevPort;
for( pPort = pIniLocalMon->pIniPort;
pPort && pPort != pIniPort;
pPort = pPort->pNext){
pPrevPort = pPort;
}
if (pPort) { // found the port
if (pPort == pIniLocalMon->pIniPort) {
pIniLocalMon->pIniPort = pPort->pNext;
} else {
pPrevPort->pNext = pPort->pNext;
}
FreeSplMem(pPort);
return TRUE;
}
else // port not found
return FALSE;
}
BOOL
RemoveDosDeviceDefinition(
PINIPORT pIniPort
)
/*++
Routine Description:
Removes the NONSPOOLED.. dos device definition created by localmon
Arguments:
pIniPort : Pointer to the INIPORT
Return Value:
TRUE on success, FALSE on error
--*/
{
WCHAR TempDosDeviceName[MAX_PATH];
if( ERROR_SUCCESS != StrNCatBuffW( TempDosDeviceName, COUNTOF(TempDosDeviceName),
L"NONSPOOLED_", pIniPort->pName, NULL ))
return FALSE;
LcmRemoveColon(TempDosDeviceName);
return DefineDosDevice(DDD_REMOVE_DEFINITION, TempDosDeviceName, NULL);
}
// @@BEGIN_DDKSPLIT
DWORD
HandleLptQueryRemove(
LPVOID pData
)
{
DWORD dwRet = NO_ERROR;
PINIPORT pIniPort = (PINIPORT)pData;
SPLASSERT(pIniPort && pIniPort->signature == IPO_SIGNATURE
&& pIniPort->hNotify != NULL );
LcmEnterSplSem();
//
// Fix is not multi-thread safe now
//
if ( pIniPort->Status & PP_STARTDOC ) {
dwRet = ERROR_BUSY;
goto Done;
}
// InitializeCriticalSection(pIniPort->&CritSection);
CloseHandle(pIniPort->hFile);
SplUnregisterForDeviceEvents(pIniPort->hNotify);
pIniPort->hNotify = NULL;
pIniPort->hFile = INVALID_HANDLE_VALUE;
Done:
LcmLeaveSplSem();
return dwRet;
}
// @@END_DDKSPLIT
BOOL
ValidateDosDevicePort(
PINIPORT pIniPort
)
/*++
Routine Description:
Checks if the given port corresponds to a dos device.
For a dos device port the following is done:
-- Dos device definition for the NONSPOOLED.. is created
-- CreateFile is done on the NONSPOOLED.. port
Arguments:
pIniPort : Pointer to the INIPORT
Return Value:
TRUE on all validations passing, FALSE otherwise
Side effect:
For dos devices :
a. CreateFile is called on the NONSPOOLED.. name
b. PP_DOSDEVPORT flag is set
c. pIniPort->pDeviceName is set to the first string found on
QueryDosDefition this could be used to see if the definition changed
(ex. when user did a net use lpt1 \\server\printer the connection
is effective only when the user is logged in)
d. PP_COMM_PORT is set for real LPT/COM port
(ie. GetCommTimeouts worked, not a net use lpt1 case)
--*/
{
DCB dcb;
COMMTIMEOUTS cto;
WCHAR TempDosDeviceName[MAX_PATH];
HANDLE hToken = NULL;
WCHAR DeviceNames[MAX_PATH];
WCHAR DosDeviceName[MAX_PATH];
WCHAR NewNtDeviceName[MAX_PATH];
WCHAR *pDeviceNames=DeviceNames;
BOOL bRet = FALSE;
LPWSTR pDeviceName = NULL;
hToken = RevertToPrinterSelf();
if (!hToken)
goto Done;
if( ERROR_SUCCESS != StrNCatBuffW( DosDeviceName, COUNTOF(DosDeviceName),
pIniPort->pName, NULL ))
goto Done;
LcmRemoveColon(DosDeviceName);
//
// If the port is not a dos device port nothing to do -- return success
//
if ( !QueryDosDevice(DosDeviceName, DeviceNames, COUNTOF (DeviceNames)) ) {
bRet = TRUE;
goto Done;
}
pDeviceName = AllocSplStr(pDeviceNames);
if ( !pDeviceName )
goto Done;
if( ERROR_SUCCESS != StrNCatBuffW( NewNtDeviceName, COUNTOF(NewNtDeviceName),
szLcmDeviceNameHeader, pIniPort->pName, NULL ))
goto Done;
LcmRemoveColon(NewNtDeviceName);
//
// Search for the first non-matching name in pDeviceNames list.
//
while ( lstrcmpi(pDeviceNames, NewNtDeviceName) == 0 ) {
pDeviceNames+=wcslen(pDeviceNames)+1;
}
if( ERROR_SUCCESS != StrNCatBuffW( TempDosDeviceName, COUNTOF(TempDosDeviceName),
L"NONSPOOLED_", pIniPort->pName, NULL ))
goto Done;
LcmRemoveColon(TempDosDeviceName);
//
// Delete any existing definition for TempDosDeviceName. This ensures that
// there exists only one definition for the nonspooled_port device name.
//
DefineDosDevice(DDD_REMOVE_DEFINITION, TempDosDeviceName, NULL);
DefineDosDevice(DDD_RAW_TARGET_PATH, TempDosDeviceName, pDeviceNames);
ImpersonatePrinterClient(hToken);
hToken = NULL;
if( ERROR_SUCCESS != StrNCatBuffW( TempDosDeviceName, COUNTOF(TempDosDeviceName),
L"\\\\.\\NONSPOOLED_", pIniPort->pName, NULL ))
goto Done;
LcmRemoveColon(TempDosDeviceName);
pIniPort->hFile = CreateFile(TempDosDeviceName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL |
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
//
// If CreateFile fails remove redirection and fail the call
//
if ( pIniPort->hFile == INVALID_HANDLE_VALUE ) {
(VOID)RemoveDosDeviceDefinition(pIniPort);
goto Done;
}
pIniPort->Status |= PP_DOSDEVPORT;
SetEndOfFile(pIniPort->hFile);
if ( IS_COM_PORT (pIniPort->pName) ) {
if ( GetCommState(pIniPort->hFile, &dcb) ) {
GetCommTimeouts(pIniPort->hFile, &cto);
GetIniCommValues (pIniPort->pName, &dcb, &cto);
SetCommState (pIniPort->hFile, &dcb);
cto.WriteTotalTimeoutConstant = WRITE_TOTAL_TIMEOUT;
cto.WriteTotalTimeoutMultiplier = 0;
cto.ReadTotalTimeoutConstant = READ_TOTAL_TIMEOUT;
cto.ReadIntervalTimeout = READ_INTERVAL_TIMEOUT;
SetCommTimeouts(pIniPort->hFile, &cto);
pIniPort->Status |= PP_COMM_PORT;
} else {
DBGMSG(DBG_WARNING,
("ERROR: Failed GetCommState pIniPort->hFile %x\n",pIniPort->hFile) );
}
} else if ( IS_LPT_PORT (pIniPort->pName) ) {
if ( GetCommTimeouts(pIniPort->hFile, &cto) ) {
cto.WriteTotalTimeoutConstant =
GetProfileInt(szWindows,
szINIKey_TransmissionRetryTimeout,
45 );
cto.WriteTotalTimeoutConstant*=1000;
SetCommTimeouts(pIniPort->hFile, &cto);
// @@BEGIN_DDKSPLIT
hToken = RevertToPrinterSelf();
pIniPort->hNotify = SplRegisterForDeviceEvents(
pIniPort->hFile,
(LPVOID)pIniPort,
HandleLptQueryRemove);
ImpersonatePrinterClient(hToken);
hToken = NULL;
// @@END_DDKSPLIT
pIniPort->Status |= PP_COMM_PORT;
} else {
DBGMSG(DBG_WARNING,
("ERROR: Failed GetCommTimeouts pIniPort->hFile %x\n",pIniPort->hFile) );
}
}
FreeSplStr( pIniPort->pDeviceName );
pIniPort->pDeviceName = pDeviceName;
bRet = TRUE;
Done:
if (hToken)
ImpersonatePrinterClient(hToken);
if ( !bRet && pDeviceName )
FreeSplStr(pDeviceName);
return bRet;
}
BOOL
FixupDosDeviceDefinition(
PINIPORT pIniPort
)
/*++
Routine Description:
Called before every StartDocPort for a DOSDEVPORT. The routine will check if
the dos device defintion has changed (if a user logged and his connection
is remembered). Also for a connection case the CreateFile is called since
that needs to be done per job
Arguments:
pIniPort : Pointer to the INIPORT
Return Value:
TRUE on all validations passing, FALSE otherwise
--*/
{
WCHAR DeviceNames[MAX_PATH];
WCHAR DosDeviceName[MAX_PATH];
HANDLE hToken;
//
// If the port is not a real LPT port we open it per job
// @@BEGIN_DDKSPLIT
// Also parallel ports could be closed on QUERYREMOVE if user undocks
// then it will be opened on next job's StartDocPort
// @@END_DDKSPLIT
//
if ( !(pIniPort->Status & PP_COMM_PORT) ||
pIniPort->hFile == INVALID_HANDLE_VALUE )
return ValidateDosDevicePort(pIniPort);
if( ERROR_SUCCESS != StrNCatBuffW( DosDeviceName, COUNTOF (DosDeviceName),
pIniPort->pName, NULL ))
return FALSE;
LcmRemoveColon(DosDeviceName);
hToken = RevertToPrinterSelf();
if (!hToken) {
return FALSE;
}
if ( !QueryDosDevice(DosDeviceName, DeviceNames, COUNTOF (DeviceNames) ) ) {
ImpersonatePrinterClient(hToken);
return FALSE;
}
//
// If strings are same then definition has not changed
//
if ( !lstrcmpi(DeviceNames, pIniPort->pDeviceName) )
{
ImpersonatePrinterClient(hToken);
return TRUE;
}
(VOID)RemoveDosDeviceDefinition(pIniPort);
CloseHandle(pIniPort->hFile);
pIniPort->hFile = INVALID_HANDLE_VALUE;
// @@BEGIN_DDKSPLIT
if ( pIniPort->hNotify ) {
SplUnregisterForDeviceEvents(pIniPort->hNotify);
pIniPort->hNotify = NULL;
}
// @@END_DDKSPLIT
pIniPort->Status &= ~(PP_COMM_PORT | PP_DOSDEVPORT);
FreeSplStr(pIniPort->pDeviceName);
pIniPort->pDeviceName = NULL;
ImpersonatePrinterClient(hToken);
return ValidateDosDevicePort(pIniPort);
}