|
|
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
xcv.c
Author:
Steve Wilson (SWilson) March 25, 1997
Revision History:
Ali Naqvi (alinaqvi) October 17, 2001 Changed IniXcv to keep IniMonitor rather than Monitor2. This way we can keep a refcount on IniMonitor, preventing the monitor to be deleted when in use. We are going to use the IniMonitor to get the Monitor2.
--*/
#include <precomp.h>
#include <offsets.h>
PINIXCV CreateXcvEntry( PCWSTR pszMachine, PCWSTR pszName, PINIMONITOR pIniMonitor, PINISPOOLER pIniSpooler, HANDLE hXcv );
VOID DeleteXcvEntry( PINIXCV pIniXcv );
BOOL SplXcvOpenPort( PCWSTR pszMachine, PCWSTR pszObject, DWORD dwType, PPRINTER_DEFAULTS pDefault, PHANDLE phXcv, PINISPOOLER pIniSpooler );
INIXCV IniXcvStart;
typedef struct { PWSTR pszMethod; BOOL (*pfn)(PINIXCV pIniXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus, PINISPOOLER pIniSpooler ); } XCV_METHOD, *PXCV_METHOD;
XCV_METHOD gpXcvMethod[] = { {L"DeletePort", XcvDeletePort}, {L"AddPort", XcvAddPort}, {NULL, NULL} };
BOOL LocalXcvData( HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus ) { PINIXCV pIniXcv = ((PSPOOL) hXcv)->pIniXcv; BOOL bReturn;
if (!ValidateXcvHandle(pIniXcv)) return ROUTER_UNKNOWN;
bReturn = SplXcvData(hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded, pdwStatus, pIniXcv->pIniSpooler);
return bReturn; }
BOOL SplXcvData( HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus, PINISPOOLER pIniSpooler ) { PINIPORT pIniPort; BOOL rc = FALSE; BOOL bCallXcvData = FALSE; PINIXCV pIniXcv = ((PSPOOL) hXcv)->pIniXcv; DWORD i;
SPLASSERT(pIniXcv->pIniMonitor->Monitor2.pfnXcvDataPort);
//
// Check to see whether the pointers we use always are not NULL
//
if (pdwStatus && pszDataName && pcbOutputNeeded) { rc = TRUE; } else { SetLastError(ERROR_INVALID_PARAMETER); }
if (rc) { //
// Execute well-known methods
//
for(i = 0 ; gpXcvMethod[i].pszMethod && wcscmp(gpXcvMethod[i].pszMethod, pszDataName) ; ++i) ;
if (gpXcvMethod[i].pszMethod) {
PINIPORT pIniPort = NULL;
if (!_wcsicmp(gpXcvMethod[i].pszMethod, L"AddPort")) { //
// Before we use the pInputData buffer, we need to check if the string
// is NULL terminated somewhere inside it.
//
if (pInputData && cbInputData && IsStringNullTerminatedInBuffer((PWSTR)pInputData, cbInputData / sizeof(WCHAR))) { EnterSplSem();
//
// Port name is the first field in the input structure. Keep Refcount on
// IniPort while outside CS.
//
pIniPort = FindPort(pInputData, pIniSpooler); if ( pIniPort ) { INCPORTREF(pIniPort); }
LeaveSplSem();
//
// If this pIniPort doesn't have a monitor associated with it, it is
// a temporary port. We will allow it to be added, if there still is
// no monitor associated with it later, we will simply use this
// structure again.
//
if (pIniPort && !(pIniPort->Status & PP_PLACEHOLDER)) { rc = TRUE; *pdwStatus = ERROR_ALREADY_EXISTS; } else { bCallXcvData = TRUE; } } else { SetLastError(ERROR_INVALID_DATA); rc = FALSE; } } else { bCallXcvData = TRUE; }
//
// Don't make the function call if we do AddPort and the port already exists.
// If it is a placeholder, that's OK.
//
if (bCallXcvData) { rc = (*gpXcvMethod[i].pfn)( pIniXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded, pdwStatus, pIniSpooler); }
if(pIniPort) { EnterSplSem(); DECPORTREF(pIniPort); LeaveSplSem(); }
} else { *pdwStatus = (*pIniXcv->pIniMonitor->Monitor2.pfnXcvDataPort)( pIniXcv->hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded ); rc = TRUE; } }
return rc; }
DWORD XcvOpen( PCWSTR pszServer, PCWSTR pszObject, DWORD dwObjectType, PPRINTER_DEFAULTS pDefault, PHANDLE phXcv, PINISPOOLER pIniSpooler ) { BOOL bRet; DWORD dwRet; DWORD dwLastError;
if (dwObjectType == XCVPORT || dwObjectType == XCVMONITOR) { bRet = SplXcvOpenPort( pszServer, pszObject, dwObjectType, pDefault, phXcv, pIniSpooler);
if (!bRet) { dwLastError = GetLastError();
if (dwLastError == ERROR_INVALID_NAME) dwRet = ROUTER_UNKNOWN; else if (dwLastError == ERROR_UNKNOWN_PORT)
// This is a case where a port exists without an associated port monitor
// (i.e. a masq port), we need to give the partial print provider a chance
// to intercept the XCV call
//
dwRet = ROUTER_UNKNOWN; else dwRet = ROUTER_STOP_ROUTING; } else { dwRet = ROUTER_SUCCESS; }
} else { dwRet = ROUTER_UNKNOWN; }
return dwRet; }
BOOL SplXcvOpenPort( PCWSTR pszMachine, PCWSTR pszObject, DWORD dwType, PPRINTER_DEFAULTS pDefault, PHANDLE phXcv, PINISPOOLER pIniSpooler ) { PINIMONITOR pIniMonitor = NULL; PINIPORT pIniPort = NULL; BOOL rc = FALSE; DWORD dwStatus; HANDLE hMonitor; PSPOOL pSpool; PINIXCV pIniXcv = NULL;
EnterSplSem();
if (dwType == XCVMONITOR) { pIniMonitor = FindMonitor(pszObject, pIniSpooler); } else { pIniPort = FindPort(pszObject, pIniSpooler);
if(pIniPort && (pIniPort->Status & PP_MONITOR)) pIniMonitor = pIniPort->pIniMonitor; }
if (pIniMonitor) {
if (!pIniMonitor->Monitor2.pfnXcvOpenPort || !pIniMonitor->Monitor2.pfnXcvDataPort || !pIniMonitor->Monitor2.pfnXcvClosePort) {
SetLastError(ERROR_INVALID_PRINT_MONITOR);
} else { //
// Keeping a RefCount on IniMonitor and IniPort while outside CS.
//
INCMONITORREF(pIniMonitor); LeaveSplSem();
dwStatus = CreateServerHandle( (PWSTR) pszMachine, phXcv, pDefault, pIniSpooler, PRINTER_HANDLE_XCV_PORT);
EnterSplSem();
if (dwStatus == ROUTER_SUCCESS) { // Create port handle
pSpool = *(PSPOOL *) phXcv; // *phXcv is pSpool
rc = (*pIniMonitor->Monitor2.pfnXcvOpenPort)( pIniMonitor->hMonitor, pszObject, pSpool->GrantedAccess, &hMonitor);
if (rc) { // Create Spooler XCV entry
pIniXcv = CreateXcvEntry( pszMachine, pszObject, pIniMonitor, pIniSpooler, hMonitor);
if (pIniXcv) {
pSpool->pIniXcv = pIniXcv;
} else {
(*pIniMonitor->Monitor2.pfnXcvClosePort)(hMonitor); rc = FALSE; } } } DECMONITORREF(pIniMonitor); } } else {
SetLastError(ERROR_UNKNOWN_PORT); rc = FALSE; }
LeaveSplSem();
return rc; }
PINIXCV CreateXcvEntry( PCWSTR pszMachine, PCWSTR pszName, PINIMONITOR pIniMonitor, PINISPOOLER pIniSpooler, HANDLE hXcv ) { PINIXCV pIniXcvPrev = &IniXcvStart; PINIXCV pIniXcv = IniXcvStart.pNext;
for(; pIniXcv ; pIniXcv = pIniXcv->pNext) pIniXcvPrev = pIniXcv;
if (!(pIniXcv = (PINIXCV) AllocSplMem(sizeof(INIXCV)))) goto Cleanup;
pIniXcv->hXcv = hXcv; pIniXcv->signature = XCV_SIGNATURE;
pIniXcv->pIniSpooler = pIniSpooler; INCSPOOLERREF( pIniSpooler );
if (pszMachine && !(pIniXcv->pszMachineName = AllocSplStr(pszMachine))) goto Cleanup;
if (pszName && !(pIniXcv->pszName = AllocSplStr(pszName))) goto Cleanup;
pIniXcv->pIniMonitor = pIniMonitor;
//
// During the lifespan of the IniXcv we keep a Refcount on the IniMonitor
//
INCMONITORREF(pIniXcv->pIniMonitor);
return pIniXcvPrev->pNext = pIniXcv;
Cleanup:
DeleteXcvEntry( pIniXcv ); return NULL; }
VOID DeleteXcvEntry( PINIXCV pIniXcv ) { if( pIniXcv ){
if( pIniXcv->pIniSpooler ){ DECSPOOLERREF( pIniXcv->pIniSpooler ); } //
// Release the IniMonitor
//
if (pIniXcv->pIniMonitor) { DECMONITORREF(pIniXcv->pIniMonitor); }
FreeSplStr(pIniXcv->pszMachineName); FreeSplStr(pIniXcv->pszName); FreeSplMem(pIniXcv); } }
BOOL XcvClose( PINIXCV pIniXcvIn ) { PINIXCV pIniXcvPrev = &IniXcvStart; PINIXCV pIniXcv = IniXcvStart.pNext; BOOL bRet;
for(; pIniXcv ; pIniXcv = pIniXcv->pNext) {
if (pIniXcv == pIniXcvIn) {
bRet = pIniXcv->pIniMonitor->Monitor2.pfnXcvClosePort(pIniXcv->hXcv);
if (bRet) { pIniXcvPrev->pNext = pIniXcv->pNext;
DeleteXcvEntry( pIniXcv ); } return bRet; }
pIniXcvPrev = pIniXcv; }
SetLastError(ERROR_INVALID_HANDLE); return FALSE; }
BOOL XcvDeletePort( PINIXCV pIniXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus, PINISPOOLER pIniSpooler ) { PINIPORT pIniPort; BOOL rc = FALSE; PWSTR pPortName = (PWSTR) pInputData;
//
// Check to see whether the pInputData is NULL terminated within its buffer
// before going down this path.
//
if (pInputData && cbInputData && IsStringNullTerminatedInBuffer((PWSTR)pInputData, cbInputData / sizeof(WCHAR))) { EnterSplSem();
pIniPort = FindPort(pPortName, pIniSpooler);
if ( !pIniPort || !(pIniPort->Status & PP_MONITOR) ) { SetLastError (*pdwStatus = ERROR_UNKNOWN_PORT); LeaveSplSem(); return FALSE; }
rc = DeletePortFromSpoolerStart(pIniPort); *pdwStatus = GetLastError ();
LeaveSplSem();
if (!rc) goto Cleanup;
*pdwStatus = (*pIniXcv->pIniMonitor->Monitor2.pfnXcvDataPort)( pIniXcv->hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
DeletePortFromSpoolerEnd(pIniPort, pIniSpooler, *pdwStatus == ERROR_SUCCESS); rc = TRUE; } else { SetLastError(ERROR_INVALID_PARAMETER); }
Cleanup:
return rc; }
BOOL XcvAddPort( PINIXCV pIniXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus, PINISPOOLER pIniSpooler ) { BOOL rc; PINIMONITOR pIniMonitor = NULL; PINIPORT pIniPort = NULL;
pIniMonitor = pIniXcv->pIniMonitor;
if (pIniMonitor) { *pdwStatus = (*pIniXcv->pIniMonitor->Monitor2.pfnXcvDataPort)( pIniXcv->hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
if (*pdwStatus == ERROR_SUCCESS) { EnterSplSem();
//
// Check to see if we have a placeholder port by the same name. If we
// do this set this as the monitor and revoke its placeholder status.
//
// This pInputData has already been validated by the "Add" method in
// XcvData itself.
//
pIniPort = FindPort(pInputData, pIniSpooler);
if (pIniPort && pIniPort->Status & PP_PLACEHOLDER) { pIniPort->pIniMonitor = pIniMonitor; pIniPort->Status |= PP_MONITOR; pIniPort->Status &= ~PP_PLACEHOLDER; } else { CreatePortEntry((PWSTR) pInputData, pIniMonitor, pIniSpooler); }
LeaveSplSem(); }
rc = TRUE;
} else { SetLastError(ERROR_INVALID_NAME); rc = FALSE; }
return rc; }
BOOL ValidateXcvHandle( PINIXCV pIniXcv ) { BOOL ReturnValue;
try { if (!pIniXcv || pIniXcv->signature != XCV_SIGNATURE) { ReturnValue = FALSE; } else { ReturnValue = TRUE; } }except (1) { ReturnValue = FALSE; }
if ( !ReturnValue ) SetLastError( ERROR_INVALID_HANDLE );
return ReturnValue; }
|