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.
 
 
 
 
 
 

1823 lines
44 KiB

/*++
Copyright (c) 1990 - 1996 Microsoft Corporation
Module Name:
monitor.c
Abstract:
This module contains all code for Monitor-based Spooler apis
LocalEnumPorts
LocalAddMonitor
LocalDeleteMonitor
LocalEnumMonitors
LocalAddPort
LocalConfigurePort
LocalDeletePort
Support Functions in monitor.c - (Warning! Do Not Add to this list!!)
CopyIniMonitorToMonitor -- KrishnaG
GetMonitorSize -- KrishnaG
Author:
Dave Snipp (DaveSn) 15-Mar-1991
Revision History:
Khaled Sedky (khaleds) 15-March-2000
- Added LocalSendRecvBidiData
Muhunthan Sivapragasam (MuhuntS) 15-Jun-1995
- Port info 2 changes
Krishna Ganugapati (KrishnaG) 2-Feb-1994
- reorganized the entire source file
Matthew Felton (mattfe) June 1994 pIniSpooler
--*/
#include <precomp.h>
#include <offsets.h>
#include <clusspl.h>
//
// Private declarations
//
HDESK ghdeskServer = NULL;
//
// Function declarations
//
LPBYTE
CopyIniMonitorToMonitor(
PINIMONITOR pIniMonitor,
DWORD Level,
LPBYTE pMonitorInfo,
LPBYTE pEnd
);
DWORD
GetMonitorSize(
PINIMONITOR pIniMonitor,
DWORD Level
);
BOOL
LocalEnumPorts(
LPWSTR pName,
DWORD Level,
LPBYTE pPorts,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
PINISPOOLER pIniSpooler;
BOOL bReturn;
pIniSpooler = FindSpoolerByNameIncRef( pName, NULL );
if( !pIniSpooler ){
return ROUTER_UNKNOWN;
}
bReturn = SplEnumPorts( pName,
Level,
pPorts,
cbBuf,
pcbNeeded,
pcReturned,
pIniSpooler );
FindSpoolerByNameDecRef( pIniSpooler );
return bReturn;
}
VOID
SplReenumeratePorts(
)
{
LPVOID pBuf;
DWORD cbNeeded, dwDontCare;
//
// EnumPorts checks for new ports enumerated by port monitors and updates
// localspl pIniPorts list
//
if ( !SplEnumPorts(NULL, 1, NULL, 0, &cbNeeded,
&dwDontCare, pLocalIniSpooler) &&
GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
(pBuf = AllocSplMem(cbNeeded)) ) {
SplEnumPorts(NULL, 1, pBuf, cbNeeded, &cbNeeded,
&dwDontCare, pLocalIniSpooler);
FreeSplMem(pBuf);
}
}
BOOL
GetPortInfo2UsingPortInfo1(
PINIMONITOR pIniMonitor,
LPWSTR pName,
LPBYTE pPorts,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL bRet;
LPPORT_INFO_1 pPortInfo1;
LPPORT_INFO_2 pPortInfo2;
DWORD cReturned;
bRet = (*pIniMonitor->Monitor2.pfnEnumPorts)(
pIniMonitor->hMonitor,
pName,
1,
pPorts,
cbBuf,
pcbNeeded,
pcReturned );
if ( !bRet ) {
//
// This is the upperbound
//
if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
*pcbNeeded += (*pcbNeeded / sizeof(PORT_INFO_1)) *
(sizeof(PORT_INFO_2) - sizeof(PORT_INFO_1));
} else {
*pcbNeeded += *pcReturned * (sizeof(PORT_INFO_2) - sizeof(PORT_INFO_1));
if ( *pcbNeeded <= cbBuf ) {
cReturned = *pcReturned;
while ( cReturned-- ) {
pPortInfo1 = (LPPORT_INFO_1) (pPorts + cReturned * sizeof(PORT_INFO_1));
pPortInfo2 = (LPPORT_INFO_2) (pPorts + cReturned * sizeof(PORT_INFO_2));
pPortInfo2->pPortName = pPortInfo1->pName;
pPortInfo2->pMonitorName = NULL;
pPortInfo2->pDescription = NULL;
pPortInfo2->fPortType = 0;
pPortInfo2->Reserved = 0;
}
} else {
*pcReturned = 0;
bRet = FALSE;
SetLastError(ERROR_INSUFFICIENT_BUFFER);
}
}
return bRet;
}
BOOL
SplEnumPorts(
LPWSTR pName,
DWORD Level,
LPBYTE pPorts,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned,
PINISPOOLER pIniSpooler
)
{
PINIMONITOR pIniMonitor;
DWORD dwIndex, cReturned=0, cbStruct, TotalcbNeeded=0;
LPBYTE pBuffer = pPorts, pTemp;
DWORD Error=0, TempError = 0;
DWORD BufferSize=cbBuf;
LPWSTR pPortName;
PINIPORT pIniPort;
HANDLE hToken;
BOOL bRemoteCall = FALSE;
if (!MyName( pName, pIniSpooler )) {
return FALSE;
}
//
// HACK: Some monitors choke if pName is non-NULL. We can make
// it NULL at this point since we know that we're using the same
// ports on the local machine.
//
pName = NULL;
if ( !ValidateObjectAccess(SPOOLER_OBJECT_SERVER,
SERVER_ACCESS_ENUMERATE,
NULL, NULL, pIniSpooler )) {
return FALSE;
}
switch (Level) {
case 1:
cbStruct = sizeof(PORT_INFO_1);
break;
case 2:
cbStruct = sizeof(PORT_INFO_2);
break;
default:
return ERROR_INVALID_LEVEL;
}
{
HRESULT hRes = CheckLocalCall();
if (hRes == S_FALSE)
{
bRemoteCall = TRUE;
}
else if (hRes != S_OK)
{
SetLastError(SCODE_CODE(hRes));
return FALSE;
}
}
//
// We revert to local system context only when the caller is remote.
// The monitors may load dlls from system32 and remote users like
// guest or anonymous logon do not have sufficient privileges for that.
// This is safe to revert to self since we do not support delegation, so
// we will never use the credentials of the remote user to go remote again.
// If the caller is logged on interactively, then we do not switch the
// context. Thus, a monitor may be able to go on the network for the port
// enumeration.
//
if (bRemoteCall && !(hToken = RevertToPrinterSelf()))
{
return FALSE;
}
for ( pIniMonitor = pIniSpooler->pIniMonitor ;
pIniMonitor ;
pIniMonitor = pIniMonitor->pNext ) {
//
// Lang monitor does not have to define this
//
if ( !pIniMonitor->Monitor2.pfnEnumPorts )
continue;
*pcReturned = 0;
*pcbNeeded = 0;
if (!(*pIniMonitor->Monitor2.pfnEnumPorts)(
pIniMonitor->hMonitor,
pName,
Level,
pPorts,
BufferSize,
pcbNeeded,
pcReturned)) {
TempError = GetLastError();
//
// Level 2 is a superset of level 1. So we can make a level 1
// call if the monitor does not support it
//
if ( Level == 2 && TempError == ERROR_INVALID_LEVEL ) {
TempError = 0;
if ( !GetPortInfo2UsingPortInfo1(pIniMonitor,
pName,
pPorts,
BufferSize,
pcbNeeded,
pcReturned) )
TempError = GetLastError();
}
if ( TempError ) {
Error = TempError;
*pcReturned = 0;
if ( TempError != ERROR_INSUFFICIENT_BUFFER ) {
*pcbNeeded = 0;
break;
}
}
} else {
//
// Now we look for new ports not in pIniPort list and add them
//
EnterSplSem();
for ( dwIndex = 0, pTemp = pPorts ;
dwIndex < *pcReturned ;
++dwIndex ) {
switch ( Level ) {
case 1:
pPortName = ((LPPORT_INFO_1)pTemp)->pName;
pTemp += sizeof(PORT_INFO_1);
break;
case 2:
pPortName = ((LPPORT_INFO_2)pTemp)->pPortName;
pTemp += sizeof(PORT_INFO_2);
break;
default:
SPLASSERT(Level == 1 || Level == 2);
}
pIniPort = FindPort(pPortName, pIniSpooler);
if ( !pIniPort ) {
CreatePortEntry(pPortName, pIniMonitor, pIniSpooler);
} else if ( !pIniPort->pIniMonitor ) {
//
// If a fake port gets eventually enumerated by a monitor,
// update the pIniPort structure (USB monitor). It is no
// longer a placeholder port at this point.
//
pIniPort->pIniMonitor = pIniMonitor;
pIniPort->Status |= PP_MONITOR;
pIniPort->Status &= ~PP_PLACEHOLDER;
}
}
LeaveSplSem();
}
cReturned += *pcReturned;
pPorts += *pcReturned * cbStruct;
if (*pcbNeeded <= BufferSize)
BufferSize -= *pcbNeeded;
else
BufferSize = 0;
TotalcbNeeded += *pcbNeeded;
}
if (bRemoteCall)
{
ImpersonatePrinterClient(hToken);
}
*pcbNeeded = TotalcbNeeded;
*pcReturned = cReturned;
if (Error) {
SetLastError(Error);
return FALSE;
} else if (TotalcbNeeded > cbBuf ) {
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
} else {
//
// Stop routing if this is a cluster'd spooler. Otherwise,
// we'll talk to win32spl, which RPCs to us again.
//
if( pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ){
return ROUTER_STOP_ROUTING;
}
return TRUE;
}
}
BOOL
LocalEnumMonitors(
LPWSTR pName,
DWORD Level,
LPBYTE pMonitors,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
PINISPOOLER pIniSpooler;
BOOL bReturn;
pIniSpooler = FindSpoolerByNameIncRef( pName, NULL );
if( !pIniSpooler ){
return ROUTER_UNKNOWN;
}
bReturn = SplEnumMonitors( pName, Level, pMonitors, cbBuf,
pcbNeeded, pcReturned, pIniSpooler );
FindSpoolerByNameDecRef( pIniSpooler );
return bReturn;
}
BOOL
SplEnumMonitors(
LPWSTR pName,
DWORD Level,
LPBYTE pMonitors,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned,
PINISPOOLER pIniSpooler
)
{
PINIMONITOR pIniMonitor;
DWORD cReturned=0, cbStruct, cb;
LPBYTE pBuffer = pMonitors;
DWORD BufferSize=cbBuf, rc;
LPBYTE pEnd;
if (!MyName( pName, pIniSpooler )) {
return ROUTER_UNKNOWN;
}
if ( !ValidateObjectAccess(SPOOLER_OBJECT_SERVER,
SERVER_ACCESS_ENUMERATE,
NULL, NULL, pIniSpooler )) {
return ROUTER_UNKNOWN;
}
switch (Level) {
case 1:
cbStruct = sizeof(MONITOR_INFO_1);
break;
case 2:
cbStruct = sizeof(MONITOR_INFO_2);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return ROUTER_UNKNOWN;
}
EnterSplSem();
for ( cb = 0, pIniMonitor = pIniSpooler->pIniMonitor ;
pIniMonitor ;
pIniMonitor = pIniMonitor->pNext ) {
//
// We'll not enumerate monitors which do not support AddPort
//
if ( pIniMonitor->Monitor2.pfnAddPort ||
pIniMonitor->Monitor2.pfnXcvOpenPort)
cb+=GetMonitorSize(pIniMonitor, Level);
}
*pcbNeeded = cb;
*pcReturned = 0;
if (cb <= cbBuf) {
pEnd=pMonitors + cbBuf;
for ( pIniMonitor = pIniSpooler->pIniMonitor ;
pIniMonitor ;
pIniMonitor = pIniMonitor->pNext ) {
//
// We'll not enumerate monitors which do not support AddPort
//
if ( !pIniMonitor->Monitor2.pfnAddPort &&
!pIniMonitor->Monitor2.pfnXcvOpenPort )
continue;
pEnd = CopyIniMonitorToMonitor(pIniMonitor, Level, pMonitors, pEnd);
switch (Level) {
case 1:
pMonitors+=sizeof(MONITOR_INFO_1);
break;
case 2:
pMonitors+=sizeof(MONITOR_INFO_2);
break;
}
(*pcReturned)++;
}
if( pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ){
//
// Stop routing, since we don't want any one else to report
// back monitors. If we're on the local machine now and
// we continue routing, win32spl will RPC back to ourself
// and re-enumerate the same ports.
//
rc = ROUTER_STOP_ROUTING;
} else {
rc = ROUTER_SUCCESS;
}
} else {
rc = ROUTER_UNKNOWN;
SetLastError(ERROR_INSUFFICIENT_BUFFER);
}
LeaveSplSem();
return rc;
}
BOOL
LocalAddPort(
LPWSTR pName,
HWND hWnd,
LPWSTR pMonitorName
)
{
PINISPOOLER pIniSpooler;
BOOL bReturn;
pIniSpooler = FindSpoolerByNameIncRef( pName, NULL );
if( !pIniSpooler ){
return ROUTER_UNKNOWN;
}
bReturn = SplAddPort( pName, hWnd, pMonitorName, pIniSpooler );
FindSpoolerByNameDecRef( pIniSpooler );
return bReturn;
}
BOOL
SplAddPort(
LPWSTR pName,
HWND hWnd,
LPWSTR pMonitorName,
PINISPOOLER pIniSpooler
)
{
PINIMONITOR pIniMonitor;
BOOL rc=FALSE;
if (!MyName( pName, pIniSpooler )) {
return FALSE;
}
if ( !ValidateObjectAccess(SPOOLER_OBJECT_SERVER,
SERVER_ACCESS_ADMINISTER,
NULL, NULL, pIniSpooler )) {
return FALSE;
}
EnterSplSem();
SPLASSERT( pIniSpooler->signature == ISP_SIGNATURE );
pIniMonitor = FindMonitor(pMonitorName, pIniSpooler);
//
// The monitor could be deleted while we are adding a port.
//
if (pIniMonitor) {
INCMONITORREF(pIniMonitor);
}
LeaveSplSem();
if ( pIniMonitor ) {
if ( pIniMonitor->Monitor2.pfnAddPort )
rc = (*pIniMonitor->Monitor2.pfnAddPort)(
pIniMonitor->hMonitor,
pName,
hWnd,
pMonitorName );
else
SetLastError(ERROR_INVALID_PARAMETER);
}
else {
SetLastError(ERROR_INVALID_NAME);
}
if (rc)
rc = AddPortToSpooler(pName, pIniMonitor, pIniSpooler);
EnterSplSem();
if (pIniMonitor) {
DECMONITORREF(pIniMonitor);
}
LeaveSplSem();
return rc;
}
BOOL
AddPortToSpooler(
PCWSTR pName,
PINIMONITOR pIniMonitor,
PINISPOOLER pIniSpooler
)
{
DWORD i, cbNeeded, cbDummy, cReturned;
PPORT_INFO_1 pPorts;
PINIPORT pIniPort;
/* If we don't already have the port in our local cache, add it:
*/
if (!(*pIniMonitor->Monitor2.pfnEnumPorts)(
pIniMonitor->hMonitor,
(PWSTR)pName,
1,
NULL,
0,
&cbNeeded,
&cReturned)) {
pPorts = AllocSplMem(cbNeeded);
if (pPorts) {
if ((*pIniMonitor->Monitor2.pfnEnumPorts)(
pIniMonitor->hMonitor,
(PWSTR)pName,
1,
(LPBYTE)pPorts,
cbNeeded,
&cbDummy,
&cReturned)) {
EnterSplSem();
for (i = 0 ; i < cReturned ; ++i) {
pIniPort = FindPort(pPorts[i].pName, pIniSpooler);
if ( !pIniPort ) {
CreatePortEntry(pPorts[i].pName, pIniMonitor, pIniSpooler);
//
// If we have a port without a monitor and it gets added at
// this time. Remove the placeholder status from it.
//
} else if ( !pIniPort->pIniMonitor ) {
pIniPort->pIniMonitor = pIniMonitor;
pIniPort->Status |= PP_MONITOR;
pIniPort->Status &= ~PP_PLACEHOLDER;
}
}
LeaveSplSem();
}
FreeSplMem(pPorts);
}
}
EnterSplSem();
SetPrinterChange(NULL,
NULL,
NULL,
PRINTER_CHANGE_ADD_PORT,
pIniSpooler);
LeaveSplSem();
return TRUE;
}
BOOL
LocalConfigurePort(
LPWSTR pName,
HWND hWnd,
LPWSTR pPortName
)
{
PINISPOOLER pIniSpooler;
BOOL bReturn;
pIniSpooler = FindSpoolerByNameIncRef( pName, NULL );
if( !pIniSpooler ){
return ROUTER_UNKNOWN;
}
bReturn = SplConfigurePort( pName, hWnd, pPortName, pIniSpooler );
FindSpoolerByNameDecRef( pIniSpooler );
return bReturn;
}
BOOL
SplConfigurePort(
LPWSTR pName,
HWND hWnd,
LPWSTR pPortName,
PINISPOOLER pIniSpooler
)
{
PINIPORT pIniPort;
BOOL rc = FALSE;
if (!MyName( pName, pIniSpooler )) {
return FALSE;
}
if ( !ValidateObjectAccess(SPOOLER_OBJECT_SERVER,
SERVER_ACCESS_ADMINISTER,
NULL, NULL, pIniSpooler )) {
return FALSE;
}
EnterSplSem();
pIniPort = FindPort(pPortName, pIniSpooler);
//
// Port could be deleted while we are configuring it.
//
if (pIniPort) {
INCPORTREF(pIniPort);
}
LeaveSplSem();
if ((pIniPort) && (pIniPort->Status & PP_MONITOR)) {
if ( !pIniPort->pIniMonitor->Monitor2.pfnConfigurePort ) {
SetLastError(ERROR_NOT_SUPPORTED);
}
else if (rc = (*pIniPort->pIniMonitor->Monitor2.pfnConfigurePort)(
pIniPort->pIniMonitor->hMonitor,
pName,
hWnd,
pPortName)) {
EnterSplSem();
SetPrinterChange(NULL,
NULL,
NULL,
PRINTER_CHANGE_CONFIGURE_PORT,
pIniSpooler);
LeaveSplSem();
}
}
else {
SetLastError(ERROR_UNKNOWN_PORT);
}
EnterSplSem();
if (pIniPort) {
DECPORTREF(pIniPort);
}
LeaveSplSem();
return rc;
}
BOOL
LocalDeletePort(
LPWSTR pName,
HWND hWnd,
LPWSTR pPortName
)
{
PINISPOOLER pIniSpooler;
BOOL bReturn;
pIniSpooler = FindSpoolerByNameIncRef( pName, NULL );
if( !pIniSpooler ){
return ROUTER_UNKNOWN;
}
bReturn = SplDeletePort( pName,
hWnd,
pPortName,
pIniSpooler );
FindSpoolerByNameDecRef( pIniSpooler );
return bReturn;
}
BOOL
SplDeletePort(
LPWSTR pName,
HWND hWnd,
LPWSTR pPortName,
PINISPOOLER pIniSpooler
)
{
PINIPORT pIniPort;
BOOL rc=FALSE;
if (!MyName( pName, pIniSpooler )) {
return FALSE;
}
if ( !ValidateObjectAccess(SPOOLER_OBJECT_SERVER,
SERVER_ACCESS_ADMINISTER,
NULL, NULL, pIniSpooler )) {
return FALSE;
}
EnterSplSem();
pIniPort = FindPort(pPortName, pIniSpooler);
if ( !pIniPort || !(pIniPort->Status & PP_MONITOR) ) {
LeaveSplSem();
SetLastError(ERROR_UNKNOWN_PORT);
return FALSE;
}
if( !pIniPort->pIniMonitor->Monitor2.pfnDeletePort ){
LeaveSplSem();
SetLastError( ERROR_NOT_SUPPORTED );
return FALSE;
}
rc = DeletePortFromSpoolerStart( pIniPort );
LeaveSplSem();
if (!rc)
goto Cleanup;
rc = (*pIniPort->pIniMonitor->Monitor2.pfnDeletePort)(
pIniPort->pIniMonitor->hMonitor,
pName,
hWnd,
pPortName);
rc = DeletePortFromSpoolerEnd(pIniPort, pIniSpooler, rc);
Cleanup:
SplOutSem();
return rc;
}
BOOL
DeletePortFromSpoolerEnd(
PINIPORT pIniPort,
PINISPOOLER pIniSpooler,
BOOL bSuccess
)
{
EnterSplSem();
if(bSuccess) {
DeletePortEntry( pIniPort );
//
// Success, delete the port data and send a notification.
//
SetPrinterChange( NULL,
NULL,
NULL,
PRINTER_CHANGE_DELETE_PORT,
pIniSpooler );
} else {
//
// Add it back. If the name is already used (e.g., just added
// while we were out of the critical section), we're in trouble,
// but there's not much we can do about it. (When we restart,
// we'll re-enumerate the duplicate name from the monitors
// anyway.)
//
DBGMSG( DBG_WARN, ( "SplDeletePort: port.DeletePort failed %d\n", GetLastError()));
LinkPortToSpooler( pIniPort, pIniSpooler );
}
LeaveSplSem();
SplOutSem();
return bSuccess;
}
BOOL
DeletePortFromSpoolerStart(
PINIPORT pIniPort
)
{
BOOL rc = FALSE;
PINISPOOLER pIniSpooler = pIniPort->pIniSpooler;
SplInSem();
if ( pIniPort->cPrinters || pIniPort->cRef || pIniPort->cJobs ) {
SetLastError(ERROR_BUSY);
goto Cleanup;
}
//
// Remove it from the linked list so that no one will try to grab
// a reference to while we're deleting it.
//
DelinkPortFromSpooler( pIniPort, pIniSpooler );
rc = TRUE;
Cleanup:
return rc;
}
BOOL
LocalAddMonitor(
LPWSTR pName,
DWORD Level,
LPBYTE pMonitorInfo
)
{
PINISPOOLER pIniSpooler;
BOOL bReturn;
pIniSpooler = FindSpoolerByNameIncRef( pName, NULL );
if( !pIniSpooler ){
return ROUTER_UNKNOWN;
}
bReturn = SplAddMonitor( pName,
Level,
pMonitorInfo,
pIniSpooler );
FindSpoolerByNameDecRef( pIniSpooler );
return bReturn;
}
BOOL
SplAddMonitor(
LPWSTR pName,
DWORD Level,
LPBYTE pMonitorInfo,
PINISPOOLER pIniSpooler
)
{
PINIMONITOR pIniMonitor;
PMONITOR_INFO_2 pMonitor = (PMONITOR_INFO_2)pMonitorInfo;
HANDLE hToken;
HKEY hKey;
LONG Status;
BOOL rc = FALSE;
DWORD dwPathLen = 0;
if (!MyName( pName, pIniSpooler )) {
return FALSE;
}
if ( !ValidateObjectAccess(SPOOLER_OBJECT_SERVER,
SERVER_ACCESS_ADMINISTER,
NULL, NULL, pIniSpooler )) {
return FALSE;
}
if (Level != 2) {
SetLastError( ERROR_INVALID_LEVEL );
return FALSE;
}
if (!pMonitor ||
!pMonitor->pName ||
!*pMonitor->pName) {
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
if (!pMonitor->pEnvironment ||
!*pMonitor->pEnvironment ||
lstrcmpi(pMonitor->pEnvironment, szEnvironment)) {
SetLastError( ERROR_INVALID_ENVIRONMENT );
return FALSE;
}
if (!pMonitor->pDLLName ||
!*pMonitor->pDLLName ){
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
EnterSplSem();
if (FindMonitor(pMonitor->pName, pIniSpooler)) {
LeaveSplSem();
SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
return FALSE;
}
hToken = RevertToPrinterSelf();
pIniMonitor = CreateMonitorEntry(pMonitor->pDLLName,
pMonitor->pName,
pIniSpooler);
if (pIniMonitor != (PINIMONITOR)-1) {
WCHAR szRegistryRoot[MAX_PATH];
PINISPOOLER pIniSpoolerMonitor;
HANDLE hKeyOut;
LPCWSTR pszPathOut;
//
// Note that even though this is built once per pIniSpooler, the
// list of monitors is the same for all spoolers. However, the
// ports that the monitor returns from EnumPorts is different for
// each pIniSpooler (for clustering).
//
// If it's not local, then it could be a cached win32 monitor.
//
if( pIniSpooler->SpoolerFlags & SPL_TYPE_LOCAL ){
pIniSpoolerMonitor = pLocalIniSpooler;
} else {
pIniSpoolerMonitor = pIniSpooler;
}
//
// Build the registry path. In some cases it's a relative
// path from hckRoot; other times it's a hard coded path from
// HKLM (e.g., win32spl).
//
GetRegistryLocation( pIniSpoolerMonitor->hckRoot,
pIniSpoolerMonitor->pszRegistryMonitors,
&hKeyOut,
&pszPathOut );
Status = StrNCatBuff( szRegistryRoot,
COUNTOF(szRegistryRoot),
pszPathOut,
L"\\",
pMonitor->pName,
NULL );
if (Status == ERROR_SUCCESS)
{
Status = RegCreateKeyEx( hKeyOut,
szRegistryRoot,
0,
NULL,
0,
KEY_WRITE,
NULL,
&hKey,
NULL );
if (Status == ERROR_SUCCESS) {
Status = RegSetValueEx( hKey,
L"Driver",
0,
REG_SZ,
(LPBYTE)pMonitor->pDLLName,
(wcslen(pMonitor->pDLLName) + 1)*sizeof(WCHAR));
if (Status == ERROR_SUCCESS) {
rc = TRUE;
} else {
SetLastError( Status );
}
RegCloseKey(hKey);
} else {
SetLastError( Status );
}
}
else
{
SetLastError(Status);
}
}
if(!ImpersonatePrinterClient(hToken))
{
rc = FALSE;
}
//
// Bug 54843 if this fails we could still have a IniMonitor on the linked list that
// is BAD, it should be removed.
// Note *maybe* we do this because a monitor might fail to initialize
// but will correctly function next time you reboot, like hpmon ( dlc doesn't become active until
// the next reboot. Please Verify.
LeaveSplSem();
if ( !rc ) {
DBGMSG( DBG_WARNING, ("SplAddMonitor failed %d\n", GetLastError() ));
}
return rc;
}
BOOL
LocalDeleteMonitor(
LPWSTR pName,
LPWSTR pEnvironment,
LPWSTR pMonitorName
)
{
PINISPOOLER pIniSpooler;
BOOL bReturn;
pIniSpooler = FindSpoolerByNameIncRef( pName, NULL );
if( !pIniSpooler ){
return ROUTER_UNKNOWN;
}
bReturn = SplDeleteMonitor( pName,
pEnvironment,
pMonitorName,
pIniSpooler );
FindSpoolerByNameDecRef( pIniSpooler );
return bReturn;
}
BOOL
SplDeleteMonitor(
LPWSTR pName,
LPWSTR pEnvironment,
LPWSTR pMonitorName,
PINISPOOLER pIniSpooler
)
{
BOOL Remote=FALSE;
PINIMONITOR pIniMonitor;
PINIPORT pIniPort, pIniPortNext;
HKEY hKeyMonitors, hKey;
LONG Status;
BOOL rc = FALSE;
HANDLE hToken;
HANDLE hKeyOut;
LPCWSTR pszPathOut;
if (pName && *pName) {
if (!MyName( pName, pIniSpooler )) {
return FALSE;
} else {
Remote=TRUE;
}
}
if ((pMonitorName == NULL) || (*pMonitorName == L'\0')) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if ( !ValidateObjectAccess(SPOOLER_OBJECT_SERVER,
SERVER_ACCESS_ADMINISTER,
NULL, NULL, pIniSpooler )) {
return FALSE;
}
EnterSplSem();
if (!(pIniMonitor=(PINIMONITOR)FindMonitor(pMonitorName,
pIniSpooler))) {
SetLastError(ERROR_UNKNOWN_PRINT_MONITOR);
LeaveSplSem();
return FALSE;
}
if ( pIniMonitor->cRef ) {
SetLastError(ERROR_PRINT_MONITOR_IN_USE);
LeaveSplSem();
return FALSE;
}
pIniPort = pIniSpooler->pIniPort;
while (pIniPort) {
if ((pIniPort->pIniMonitor == pIniMonitor) &&
(pIniPort->cPrinters || pIniPort->cRef)) {
SetLastError(ERROR_BUSY);
LeaveSplSem();
return FALSE;
}
pIniPort = pIniPort->pNext;
}
hToken = RevertToPrinterSelf();
GetRegistryLocation( pIniSpooler->hckRoot,
pIniSpooler->pszRegistryMonitors,
&hKeyOut,
&pszPathOut );
Status = SplRegOpenKey(hKeyOut,
pszPathOut,
KEY_READ | KEY_WRITE | DELETE,
&hKeyMonitors,
pIniSpooler);
if (Status == ERROR_SUCCESS)
{
Status = SplRegOpenKey(hKeyMonitors,
pMonitorName,
KEY_READ | KEY_WRITE,
&hKey,
pIniSpooler);
if (Status == ERROR_SUCCESS)
{
Status = DeleteSubkeys(hKey, pIniSpooler);
SplRegCloseKey(hKey, pIniSpooler);
if (Status == ERROR_SUCCESS)
Status = SplRegDeleteKey(hKeyMonitors, pMonitorName, pIniSpooler);
}
SplRegCloseKey(hKeyMonitors, pIniSpooler);
}
if (Status == ERROR_SUCCESS) {
pIniPort = pIniSpooler->pIniPort;
while (pIniPort) {
pIniPortNext = pIniPort->pNext;
if (pIniPort->pIniMonitor == pIniMonitor)
DeletePortEntry(pIniPort);
pIniPort = pIniPortNext;
}
RemoveFromList((PINIENTRY *)&pIniSpooler->pIniMonitor,
(PINIENTRY)pIniMonitor);
FreeIniMonitor( pIniMonitor );
rc = TRUE;
}
if (Status != ERROR_SUCCESS)
SetLastError(Status);
if(!ImpersonatePrinterClient(hToken))
{
rc = FALSE;
}
LeaveSplSem();
return rc;
}
LPBYTE
CopyIniMonitorToMonitor(
PINIMONITOR pIniMonitor,
DWORD Level,
LPBYTE pMonitorInfo,
LPBYTE pEnd
)
{
LPWSTR *pSourceStrings, *SourceStrings;
DWORD j;
DWORD *pOffsets;
switch (Level) {
case 1:
pOffsets = MonitorInfo1Strings;
break;
case 2:
pOffsets = MonitorInfo2Strings;
break;
default:
return pEnd;
}
for (j=0; pOffsets[j] != -1; j++) {
}
SourceStrings = pSourceStrings = AllocSplMem(j * sizeof(LPWSTR));
if (!SourceStrings) {
DBGMSG(DBG_WARNING, ("Failed to alloc Port source strings.\n"));
return pEnd;
}
switch (Level) {
case 1:
*pSourceStrings++=pIniMonitor->pName;
break;
case 2:
*pSourceStrings++=pIniMonitor->pName;
*pSourceStrings++=szEnvironment;
*pSourceStrings++=pIniMonitor->pMonitorDll;
break;
}
pEnd = PackStrings(SourceStrings, pMonitorInfo, pOffsets, pEnd);
FreeSplMem(SourceStrings);
return pEnd;
}
DWORD
GetMonitorSize(
PINIMONITOR pIniMonitor,
DWORD Level
)
{
DWORD cb=0;
switch (Level) {
case 1:
cb=sizeof(MONITOR_INFO_1) + wcslen(pIniMonitor->pName)*sizeof(WCHAR) +
sizeof(WCHAR);
break;
case 2:
cb = wcslen(pIniMonitor->pName) + 1 + wcslen(pIniMonitor->pMonitorDll) + 1
+ wcslen(szEnvironment) + 1;
cb *= sizeof(WCHAR);
cb += sizeof(MONITOR_INFO_2);
break;
default:
cb = 0;
break;
}
return cb;
}
BOOL
LocalAddPortEx(
LPWSTR pName,
DWORD Level,
LPBYTE pBuffer,
LPWSTR pMonitorName
)
{
return ( SplAddPortEx( pName,
Level,
pBuffer,
pMonitorName,
pLocalIniSpooler ));
}
BOOL
SplAddPortEx(
LPWSTR pName,
DWORD Level,
LPBYTE pBuffer,
LPWSTR pMonitorName,
PINISPOOLER pIniSpooler
)
{
PINIMONITOR pIniMonitor;
BOOL rc=FALSE;
DWORD i, cbNeeded, cReturned, cbDummy;
PPORT_INFO_1 pPorts = NULL;
PINIPORT pIniPort;
SplOutSem();
//
// At this time we do not know if the server name in pName refers to our local
// machine. We are trying to add the server name to the name cache. The name
// cache functions decide if the name refers to the local machine and if positive,
// add an entry for it in the cache.
//
CacheAddName(pName);
if (!MyName( pName, pIniSpooler )) {
return FALSE;
}
if ( !ValidateObjectAccess(SPOOLER_OBJECT_SERVER,
SERVER_ACCESS_ADMINISTER,
NULL, NULL, pIniSpooler )) {
return FALSE;
}
EnterSplSem();
pIniMonitor = FindMonitor(pMonitorName, pIniSpooler);
LeaveSplSem();
if (!pIniMonitor) {
SetLastError(ERROR_INVALID_NAME);
return(FALSE);
}
if (pIniMonitor->Monitor2.pfnAddPortEx) {
rc = (*pIniMonitor->Monitor2.pfnAddPortEx)(
pIniMonitor->hMonitor,
pName,
Level,
pBuffer,
pMonitorName);
}
if (!rc) {
return(FALSE);
}
if (!(*pIniMonitor->Monitor2.pfnEnumPorts)(
pIniMonitor->hMonitor,
pName,
1,
NULL,
0,
&cbNeeded,
&cReturned)) {
pPorts = AllocSplMem(cbNeeded);
}
if (pPorts) {
if ((*pIniMonitor->Monitor2.pfnEnumPorts)(
pIniMonitor->hMonitor,
pName,
1,
(LPBYTE)pPorts,
cbNeeded,
&cbDummy,
&cReturned)) {
EnterSplSem();
for (i = 0; i < cReturned; i++) {
pIniPort = FindPort(pPorts[i].pName, pIniSpooler);
if ( !pIniPort ) {
CreatePortEntry(pPorts[i].pName, pIniMonitor, pIniSpooler);
} else if ( !pIniPort->pIniMonitor ) {
pIniPort->pIniMonitor = pIniMonitor;
pIniPort->Status |= PP_MONITOR;
}
}
LeaveSplSem();
}
FreeSplMem(pPorts);
}
EnterSplSem();
SetPrinterChange(NULL,
NULL,
NULL,
PRINTER_CHANGE_ADD_PORT,
pIniSpooler);
LeaveSplSem();
return rc;
}
VOID
LinkPortToSpooler(
PINIPORT pIniPort,
PINISPOOLER pIniSpooler
)
/*++
Routine Description:
Links a pIniPort onto the pIniSpooler.
Arguments:
pIniPort - Port to link; must not already be on a ll.
pIniSpooler - Provides ll for pIniPort.
Return Value:
--*/
{
SplInSem();
SPLASSERT( !pIniPort->pIniSpooler );
pIniPort->pNext = pIniSpooler->pIniPort;
pIniPort->pIniSpooler = pIniSpooler;
pIniSpooler->pIniPort = pIniPort;
}
VOID
DelinkPortFromSpooler(
PINIPORT pIniPort,
PINISPOOLER pIniSpooler
)
/*++
Routine Description:
Remove a pIniPort from a pIniSpooler->pIniPort linked list. The
pIniPort may or may not be on the list; if it isn't, then this
routine does nothing.
Generic delink code ripped out into a subroutine.
The refcount on pIniPort must be zero. Anyone that uses pIniPort
must hold a reference, since it may be deleted outside the
SplSem when cRef==0.
Arguments:
pIniPort - Port to delink from the list. May or may not be on
pIniSpooler->pIniPort.
pIniSpooler - Linked list from which the pIniPort will be removed.
Return Value:
--*/
{
PINIPORT *ppCurPort;
SplInSem();
SPLASSERT( !pIniPort->cRef );
//
// Keep searching for pIniPort until we hit the end of the
// list or we've found it.
//
for( ppCurPort = &pIniSpooler->pIniPort;
*ppCurPort && *ppCurPort != pIniPort;
ppCurPort = &((*ppCurPort)->pNext )){
; // Don't do anything.
}
//
// If we found it, delink it.
//
if( *ppCurPort ){
*ppCurPort = (*ppCurPort)->pNext;
//
// Null out the back pointer since we have removed it from
// the pIniSpooler.
//
pIniPort->pIniSpooler = NULL;
}
}
/*++
Function Name:
LocalSendRecvBidiData
Description:
This function is the providor point of communicating with
Monitors supporting BIDI data. It allows the providor to
set data in the printer and query data from the printer
Parameters:
hPrinter : This could be a Printer/Port Handle
dwAccessBit : Priverledges allowed to the accessing thread
pAction :
pReqData : Request encapsulatig the queries in an array
ppResData : Response returned to client in an array of Data
Return Value:
Win32 Error Code
--*/
DWORD
LocalSendRecvBidiData(
IN HANDLE hPrinter,
IN LPCTSTR pAction,
IN PBIDI_REQUEST_CONTAINER pReqData,
OUT PBIDI_RESPONSE_CONTAINER* ppResData
)
{
DWORD dwRet = ERROR_SUCCESS;
PSPOOL pSpool = (PSPOOL)hPrinter;
PINIPORT pIniPort = NULL;
PINIMONITOR pIniMonitor = NULL;
PINIMONITOR pIniLangMonitor = NULL;
PINIPRINTER pIniPrinter = NULL;
LPTSTR pszPrinter = NULL;
TCHAR szFullPrinter[ MAX_UNC_PRINTER_NAME ];
EnterSplSem();
//
// Process of validating the parameters
//
if((!pAction || !*pAction) ||
(!pReqData && !ppResData))
{
dwRet = ERROR_INVALID_PARAMETER;
}
else
{
if (!ValidateSpoolHandle( pSpool, PRINTER_HANDLE_SERVER ))
{
dwRet = ERROR_INVALID_HANDLE;
}
else
{
if(pSpool->TypeofHandle & PRINTER_HANDLE_PRINTER)
{
if ((pIniPrinter = pSpool->pIniPrinter) &&
(pIniPort = FindIniPortFromIniPrinter(pIniPrinter)) &&
(pIniPort->Status & PP_MONITOR))
{
pIniLangMonitor = pIniPrinter->pIniDriver->pIniLangMonitor;
if (pIniLangMonitor &&
!pIniLangMonitor->Monitor2.pfnSendRecvBidiDataFromPort)
{
pIniLangMonitor = NULL;
}
if (pIniPrinter->pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER)
{
pszPrinter = szFullPrinter;
dwRet = StatusFromHResult(StringCchPrintf(szFullPrinter,
COUNTOF(szFullPrinter),
L"%ws\\%ws",
pIniPrinter->pIniSpooler->pMachineName,
pIniPrinter->pName));
}
else
{
pszPrinter = pIniPrinter->pName;
}
}
else
{
dwRet = ERROR_INVALID_HANDLE;
}
}
else if(pSpool->TypeofHandle & PRINTER_HANDLE_PORT &&
(pSpool->pIniPort->Status & PP_MONITOR))
{
pIniPort = pSpool->pIniPort;
}
else
{
dwRet = ERROR_INVALID_HANDLE;
}
if (dwRet == ERROR_SUCCESS)
{
//
// Port always must be opened
//
dwRet = StatusFromHResult(OpenMonitorPort(pIniPort,
pIniLangMonitor,
pszPrinter));
if (dwRet == ERROR_SUCCESS)
{
pIniMonitor = GetOpenedMonitor(pIniPort);
//
// Calling into the monitor
//
if(pIniMonitor->Monitor2.pfnSendRecvBidiDataFromPort)
{
LeaveSplSem();
SplOutSem();
dwRet = (*pIniMonitor->Monitor2.pfnSendRecvBidiDataFromPort)(GetMonitorHandle(pIniPort),
pSpool->GrantedAccess,
pAction,
pReqData,
ppResData);
EnterSplSem();
}
else
{
//
// Here we could use a simulation code;
//
dwRet = ERROR_NOT_SUPPORTED;
}
ReleaseMonitorPort(pIniPort);
}
}
}
}
LeaveSplSem();
return(dwRet);
}