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.
1761 lines
44 KiB
1761 lines
44 KiB
/*++
|
|
|
|
Copyright (c) 1993 - 1995 Microsoft Corporation
|
|
|
|
Abstract:
|
|
|
|
This module provides the exported API WaitForPrinterChange,
|
|
and the support functions internal to the local spooler.
|
|
|
|
Author:
|
|
|
|
Andrew Bell (AndrewBe) March 1993
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include<precomp.h>
|
|
|
|
|
|
typedef struct _NOTIFY_FIELD_TABLE {
|
|
WORD Field;
|
|
WORD Table;
|
|
WORD Offset;
|
|
} NOTIFY_FIELD_TYPE, *PNOTIFY_FIELD_TYPE;
|
|
|
|
//
|
|
// Translation table from PRINTER_NOTIFY_FIELD_* to bit vector
|
|
//
|
|
NOTIFY_FIELD_TYPE NotifyFieldTypePrinter[] = {
|
|
#define DEFINE(field, x, y, table, offset) \
|
|
{ PRINTER_NOTIFY_FIELD_##field, table, OFFSETOF(INIPRINTER, offset) },
|
|
#include <ntfyprn.h>
|
|
#undef DEFINE
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
NOTIFY_FIELD_TYPE NotifyFieldTypeJob[] = {
|
|
#define DEFINE(field, x, y, table, offset) \
|
|
{ JOB_NOTIFY_FIELD_##field, table, OFFSETOF(INIJOB, offset) },
|
|
#include <ntfyjob.h>
|
|
#undef DEFINE
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
typedef struct _NOTIFY_RAW_DATA {
|
|
PVOID pvData;
|
|
DWORD dwId;
|
|
} NOTIFY_RAW_DATA, *PNOTIFY_RAW_DATA;
|
|
|
|
//
|
|
// Currently we assume that the number of PRINTER_NOTIFY_FIELD_* elements
|
|
// will fit in one DWORD vector (32 bits). If this is ever false,
|
|
// we need to re-write this code.
|
|
//
|
|
PNOTIFY_FIELD_TYPE apNotifyFieldTypes[NOTIFY_TYPE_MAX] = {
|
|
NotifyFieldTypePrinter,
|
|
NotifyFieldTypeJob
|
|
};
|
|
|
|
DWORD adwNotifyFieldOffsets[NOTIFY_TYPE_MAX] = {
|
|
I_PRINTER_END,
|
|
I_JOB_END
|
|
};
|
|
|
|
#define NOTIFY_FIELD_TOTAL (I_PRINTER_END + I_JOB_END)
|
|
|
|
|
|
//
|
|
// Common NotifyVectors used in the system.
|
|
// NV*
|
|
//
|
|
NOTIFYVECTOR NVPrinterStatus = {
|
|
BIT(I_PRINTER_STATUS), // | BIT(I_PRINTER_STATUS_STRING),
|
|
BIT_NONE
|
|
};
|
|
|
|
NOTIFYVECTOR NVPrinterSD = {
|
|
BIT(I_PRINTER_SECURITY_DESCRIPTOR),
|
|
BIT_NONE
|
|
};
|
|
|
|
NOTIFYVECTOR NVJobStatus = {
|
|
BIT_NONE,
|
|
BIT(I_JOB_STATUS)
|
|
};
|
|
|
|
NOTIFYVECTOR NVJobStatusAndString = {
|
|
BIT_NONE,
|
|
BIT(I_JOB_STATUS) | BIT(I_JOB_STATUS_STRING)
|
|
};
|
|
|
|
NOTIFYVECTOR NVJobStatusString = {
|
|
BIT_NONE,
|
|
BIT(I_JOB_STATUS_STRING)
|
|
};
|
|
|
|
NOTIFYVECTOR NVPurge = {
|
|
BIT(I_PRINTER_STATUS),
|
|
BIT_NONE,
|
|
};
|
|
|
|
NOTIFYVECTOR NVDeletedJob = {
|
|
BIT(I_PRINTER_CJOBS),
|
|
BIT(I_JOB_STATUS)
|
|
};
|
|
|
|
NOTIFYVECTOR NVAddJob = {
|
|
BIT(I_PRINTER_CJOBS),
|
|
BIT_ALL
|
|
};
|
|
|
|
NOTIFYVECTOR NVPrinterAll = {
|
|
BIT_ALL,
|
|
BIT_NONE
|
|
};
|
|
|
|
NOTIFYVECTOR NVSpoolJob = {
|
|
BIT_NONE,
|
|
BIT(I_JOB_TOTAL_BYTES) | BIT(I_JOB_TOTAL_PAGES)
|
|
};
|
|
|
|
NOTIFYVECTOR NVWriteJob = {
|
|
BIT_NONE,
|
|
BIT(I_JOB_BYTES_PRINTED) | BIT(I_JOB_PAGES_PRINTED)
|
|
};
|
|
|
|
NOTIFYVECTOR NVJobPrinted = {
|
|
BIT_NONE,
|
|
BIT(I_JOB_BYTES_PRINTED) | BIT(I_JOB_PAGES_PRINTED) | BIT(I_JOB_STATUS)
|
|
};
|
|
|
|
|
|
//
|
|
// Forward prototypes.
|
|
//
|
|
ESTATUS
|
|
ValidateStartNotify(
|
|
PSPOOL pSpool,
|
|
DWORD fdwFilterFlags,
|
|
DWORD fdwOptions,
|
|
PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
|
|
PINIPRINTER* ppIniPrinter);
|
|
|
|
BOOL
|
|
SetSpoolChange(
|
|
PSPOOL pSpool,
|
|
PNOTIFY_RAW_DATA pNotifyRawData,
|
|
PDWORD pdwNotifyVectors,
|
|
DWORD Flags);
|
|
|
|
BOOL
|
|
SetupNotifyOptions(
|
|
PSPOOL pSpool,
|
|
PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions);
|
|
|
|
VOID
|
|
NotifyInfoTypes(
|
|
PSPOOL pSpool,
|
|
PNOTIFY_RAW_DATA pNotifyRawData,
|
|
PDWORD pdwNotifyVectors,
|
|
DWORD ChangeFlags);
|
|
|
|
BOOL
|
|
RefreshBuildInfoData(
|
|
PSPOOL pSpool,
|
|
PPRINTER_NOTIFY_INFO pInfo,
|
|
UINT cInfo,
|
|
WORD Type,
|
|
PNOTIFY_RAW_DATA pNotifyRawData);
|
|
|
|
|
|
|
|
DWORD
|
|
LocalWaitForPrinterChange(
|
|
HANDLE hPrinter,
|
|
DWORD fdwFilterFlags)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API may be called by an application if it wants to know
|
|
when the status of a printer or print server changes.
|
|
Valid events to wait for are defined by the PRINTER_CHANGE_* manifests.
|
|
|
|
Arguments:
|
|
|
|
hPrinter - A printer handle returned by OpenPrinter.
|
|
This may correspond to either a printer or a server.
|
|
|
|
fdwFilterFlags - One or more PRINTER_CHANGE_* values combined.
|
|
The function will return if any of these changes occurs.
|
|
|
|
Return Value:
|
|
|
|
Non-zero: A mask containing the change which occurred.
|
|
|
|
Zero: Either an error occurred or the handle (hPrinter) was closed
|
|
by another thread. In the latter case GetLastError returns
|
|
ERROR_INVALID_HANDLE.
|
|
|
|
When a call is made to WaitForPrinterChange, we create an event in the
|
|
SPOOL structure pointed to by the handle, to enable signaling between
|
|
the thread causing the printer change and the thread waiting for it.
|
|
|
|
When a change occurs, e.g. StartDocPrinter, the function SetPrinterChange
|
|
is called, which traverses the linked list of handles pointed to by
|
|
the PRINTERINI structure associated with that printer, and also any
|
|
open handles on the server, then signals any events which it finds
|
|
which has reuested to be informed if this change takes place.
|
|
|
|
If there is no thread currently waiting, the change flag is maintained,
|
|
so that later calls to WaitForPrinterChange can return immediately.
|
|
This ensures that changes which occur between calls will not be lost.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
PSPOOL pSpool = (PSPOOL)hPrinter;
|
|
PINIPRINTER pIniPrinter = NULL; /* Remains NULL for server */
|
|
DWORD rc = 0;
|
|
DWORD ChangeFlags = 0;
|
|
HANDLE ChangeEvent = 0;
|
|
DWORD TimeoutFlags = 0;
|
|
#if DBG
|
|
static DWORD Count = 0;
|
|
#endif
|
|
|
|
DBGMSG(DBG_NOTIFY,
|
|
("WaitForPrinterChange( %08x, %08x )\n", hPrinter, fdwFilterFlags));
|
|
|
|
EnterSplSem();
|
|
|
|
switch (ValidateStartNotify(pSpool,
|
|
fdwFilterFlags,
|
|
0,
|
|
NULL,
|
|
&pIniPrinter)) {
|
|
case STATUS_PORT:
|
|
|
|
DBGMSG(DBG_NOTIFY, ("Port with no monitor: Calling WaitForPrinterChange\n"));
|
|
LeaveSplSem();
|
|
|
|
return WaitForPrinterChange(pSpool->hPort, fdwFilterFlags);
|
|
|
|
case STATUS_FAIL:
|
|
|
|
LeaveSplSem();
|
|
return 0;
|
|
|
|
case STATUS_VALID:
|
|
break;
|
|
}
|
|
|
|
DBGMSG(DBG_NOTIFY, ("WaitForPrinterChange %08x on %ws:\n%d caller%s waiting\n",
|
|
fdwFilterFlags,
|
|
pIniPrinter ? pIniPrinter->pName : pSpool->pIniSpooler->pMachineName,
|
|
Count, Count == 1 ? "" : "s"));
|
|
|
|
//
|
|
// There may already have been a change since we last called:
|
|
//
|
|
if ((pSpool->ChangeFlags == PRINTER_CHANGE_CLOSE_PRINTER) ||
|
|
(pSpool->ChangeFlags & fdwFilterFlags)) {
|
|
|
|
if (pSpool->ChangeFlags == PRINTER_CHANGE_CLOSE_PRINTER)
|
|
ChangeFlags = 0;
|
|
else
|
|
ChangeFlags = pSpool->ChangeFlags;
|
|
|
|
DBGMSG(DBG_NOTIFY, ("No need to wait: Printer change %08x detected on %ws:\n%d remaining caller%s\n",
|
|
(ChangeFlags & fdwFilterFlags),
|
|
pIniPrinter ? pIniPrinter->pName : pSpool->pIniSpooler->pMachineName,
|
|
Count, Count == 1 ? "" : "s"));
|
|
|
|
pSpool->ChangeFlags = 0;
|
|
|
|
LeaveSplSem();
|
|
return (ChangeFlags & fdwFilterFlags);
|
|
}
|
|
|
|
ChangeEvent = CreateEvent(NULL,
|
|
EVENT_RESET_AUTOMATIC,
|
|
EVENT_INITIAL_STATE_NOT_SIGNALED,
|
|
NULL);
|
|
|
|
if ( !ChangeEvent ) {
|
|
|
|
DBGMSG( DBG_WARNING, ("CreateEvent( ChangeEvent ) failed: Error %d\n", GetLastError()));
|
|
|
|
LeaveSplSem();
|
|
return 0;
|
|
}
|
|
|
|
DBGMSG(DBG_NOTIFY, ("ChangeEvent == %x\n", ChangeEvent));
|
|
|
|
//
|
|
// SetSpoolChange checks that pSpool->ChangeEvent is non-null
|
|
// to decide whether to call SetEvent().
|
|
//
|
|
pSpool->WaitFlags = fdwFilterFlags;
|
|
pSpool->ChangeEvent = ChangeEvent;
|
|
pSpool->pChangeFlags = &ChangeFlags;
|
|
pSpool->Status |= SPOOL_STATUS_NOTIFY;
|
|
|
|
LeaveSplSem();
|
|
|
|
|
|
DBGMSG( DBG_NOTIFY,
|
|
( "WaitForPrinterChange: Calling WaitForSingleObject( %x )\n",
|
|
pSpool->ChangeEvent ));
|
|
|
|
rc = WaitForSingleObject(pSpool->ChangeEvent,
|
|
PRINTER_CHANGE_TIMEOUT_VALUE);
|
|
|
|
DBGMSG( DBG_NOTIFY,
|
|
( "WaitForPrinterChange: WaitForSingleObject( %x ) returned\n",
|
|
pSpool->ChangeEvent ));
|
|
|
|
EnterSplSem();
|
|
|
|
pSpool->Status &= ~SPOOL_STATUS_NOTIFY;
|
|
pSpool->ChangeEvent = NULL;
|
|
pSpool->pChangeFlags = NULL;
|
|
|
|
if (rc == WAIT_TIMEOUT) {
|
|
|
|
DBGMSG(DBG_INFO, ("WaitForPrinterChange on %ws timed out after %d minutes\n",
|
|
pIniPrinter ? pIniPrinter->pName : pSpool->pIniSpooler->pMachineName,
|
|
(PRINTER_CHANGE_TIMEOUT_VALUE / 60000)));
|
|
|
|
ChangeFlags |= fdwFilterFlags;
|
|
TimeoutFlags = PRINTER_CHANGE_TIMEOUT;
|
|
}
|
|
|
|
if (ChangeFlags == PRINTER_CHANGE_CLOSE_PRINTER) {
|
|
|
|
ChangeFlags = 0;
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
}
|
|
|
|
DBGMSG(DBG_NOTIFY, ("Printer change %08x detected on %ws:\n%d remaining caller%s\n",
|
|
((ChangeFlags & fdwFilterFlags) | TimeoutFlags),
|
|
pIniPrinter ? pIniPrinter->pName : pSpool->pIniSpooler->pMachineName,
|
|
Count, Count == 1 ? "" : "s"));
|
|
|
|
if (ChangeEvent && !CloseHandle(ChangeEvent)) {
|
|
|
|
DBGMSG(DBG_WARNING, ("CloseHandle( %x ) failed: Error %d\n",
|
|
ChangeEvent, GetLastError()));
|
|
}
|
|
|
|
//
|
|
// If the pSpool is pending deletion, we must free it here.
|
|
//
|
|
if (pSpool->eStatus & STATUS_PENDING_DELETION) {
|
|
|
|
FreeSplMem(pSpool);
|
|
}
|
|
|
|
LeaveSplSem();
|
|
|
|
return ((ChangeFlags & fdwFilterFlags) | TimeoutFlags);
|
|
}
|
|
|
|
BOOL
|
|
SetSpoolClosingChange(
|
|
PSPOOL pSpool)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
A print handle is closing; trigger a notification.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
return SetSpoolChange(pSpool,
|
|
NULL,
|
|
NULL,
|
|
PRINTER_CHANGE_CLOSE_PRINTER);
|
|
}
|
|
|
|
BOOL
|
|
SetSpoolChange(
|
|
PSPOOL pSpool,
|
|
PNOTIFY_RAW_DATA pNotifyRawData,
|
|
PDWORD pdwNotifyVectors,
|
|
DWORD Flags)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the event for notification or calls ReplyPrinterChangeNotification.
|
|
This is called by SetPrinterChange for every open handle on a printer
|
|
and the local server.
|
|
|
|
It should also be called when an individual handle is closed.
|
|
|
|
Assumes we're INSIDE the spooler critical section
|
|
|
|
Arguments:
|
|
|
|
pSpool -- Specifies handle that changed.
|
|
|
|
pIniJob -- Used if there is a watch on job information.
|
|
|
|
pdwNotifyVectors -- Specifies what things have changed.
|
|
|
|
Flags -- WaitForPrinterChange flags.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD ChangeFlags;
|
|
|
|
SplInSem();
|
|
|
|
if( Flags == PRINTER_CHANGE_CLOSE_PRINTER ) {
|
|
|
|
ChangeFlags = PRINTER_CHANGE_CLOSE_PRINTER;
|
|
|
|
} else {
|
|
|
|
ChangeFlags = ( pSpool->ChangeFlags | Flags ) & pSpool->WaitFlags;
|
|
}
|
|
|
|
//
|
|
// If we have STATUS_VALID set
|
|
// then we are using the new FFPCN code.
|
|
//
|
|
|
|
if ( pSpool->eStatus & STATUS_VALID ) {
|
|
|
|
NotifyInfoTypes(pSpool,
|
|
pNotifyRawData,
|
|
pdwNotifyVectors,
|
|
ChangeFlags);
|
|
|
|
}
|
|
|
|
if ( ChangeFlags ) {
|
|
|
|
pSpool->ChangeFlags = 0;
|
|
|
|
if ( pSpool->pChangeFlags ) {
|
|
|
|
*pSpool->pChangeFlags = ChangeFlags;
|
|
|
|
DBGMSG( DBG_NOTIFY, ( "SetSpoolChange: Calling SetEvent( %x )\n", pSpool->ChangeEvent ));
|
|
|
|
SetEvent(pSpool->ChangeEvent);
|
|
|
|
DBGMSG( DBG_NOTIFY, ( "SetSpoolChange: SetEvent( %x ) returned\n", pSpool->ChangeEvent ));
|
|
|
|
pSpool->pChangeFlags = NULL;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
PrinterNotificationVisible
|
|
|
|
Routine Description:
|
|
|
|
This checks to see whether the given printer handle uses the new printer
|
|
change notifications, it then checks to see whether the given printer is
|
|
a TS printer and then checks to see whether this printer is visible to the
|
|
user who opened the printer handle. This need only be called for server
|
|
handles since the fact that the user was able to open the printer handle
|
|
implies that it is visible to this.
|
|
|
|
Arguments:
|
|
|
|
pIniPrinter - NULL, or a valid pointer to the INIPRINTER for the printer
|
|
on which the change occurred.
|
|
pSpool - The printer handle which we are testing for access.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the printer notification should be delivered, FALSE if the printer
|
|
change notification should not be delivered.
|
|
|
|
--*/
|
|
BOOL
|
|
PrinterNotificationVisible(
|
|
IN PINIPRINTER pIniPrinter OPTIONAL,
|
|
IN PSPOOL pSpool
|
|
)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
|
|
//
|
|
// If the pSpool handle is a new notification handle and we have a printer
|
|
// and it is a TS printer and we can't show it, then return FALSE otherwise
|
|
// the notification can be sent.
|
|
//
|
|
if (pSpool->eStatus & STATUS_VALID &&
|
|
pIniPrinter &&
|
|
(pIniPrinter->Attributes & PRINTER_ATTRIBUTE_TS) &&
|
|
pSpool->hClientToken &&
|
|
!ShowThisPrinter(pIniPrinter, pSpool->hClientToken))
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
SetPrinterChange
|
|
|
|
Routine Description:
|
|
|
|
Calls SetSpoolChange for every open handle for the server
|
|
and printer, if specified.
|
|
|
|
Arguments:
|
|
|
|
pIniPrinter - NULL, or a valid pointer to the INIPRINTER for the printer
|
|
on which the change occurred.
|
|
|
|
Flags - PRINTER_CHANGE_* constant indicating what happened.
|
|
|
|
|
|
Note: we pass a pointer to pPrinterNotifyInfo to SetSpoolChange.
|
|
If one call needs it, it will check this parm, then create it if
|
|
necessary. This way it is retrieved only once.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
BOOL
|
|
SetPrinterChange(
|
|
PINIPRINTER pIniPrinter,
|
|
PINIJOB pIniJob,
|
|
PDWORD pdwNotifyVectors,
|
|
DWORD Flags,
|
|
PINISPOOLER pIniSpooler)
|
|
{
|
|
NOTIFY_RAW_DATA aNotifyRawData[NOTIFY_TYPE_MAX];
|
|
PSPOOL pSpool;
|
|
PINIPRINTER mypIniPrinter;
|
|
|
|
SPLASSERT( pIniSpooler->signature == ISP_SIGNATURE );
|
|
|
|
SplInSem();
|
|
|
|
if ( pIniSpooler->SpoolerFlags & SPL_PRINTER_CHANGES ) {
|
|
|
|
aNotifyRawData[0].pvData = pIniPrinter;
|
|
aNotifyRawData[0].dwId = pIniPrinter ? pIniPrinter->dwUniqueSessionID : 0;
|
|
|
|
aNotifyRawData[1].pvData = pIniJob;
|
|
aNotifyRawData[1].dwId = pIniJob ? pIniJob->JobId : 0;
|
|
|
|
if ( pIniPrinter ) {
|
|
|
|
SPLASSERT( ( pIniPrinter->signature == IP_SIGNATURE ) &&
|
|
( pIniPrinter->pIniSpooler == pIniSpooler ));
|
|
|
|
DBGMSG(DBG_NOTIFY, ("SetPrinterChange %ws; Flags: %08x\n",
|
|
pIniPrinter->pName, Flags));
|
|
|
|
for (pSpool = pIniPrinter->pSpool; pSpool; pSpool = pSpool->pNext) {
|
|
|
|
SetSpoolChange( pSpool,
|
|
aNotifyRawData,
|
|
pdwNotifyVectors,
|
|
Flags );
|
|
}
|
|
|
|
} else {
|
|
|
|
// WorkStation Caching requires a time stamp change
|
|
// any time cached data changes
|
|
|
|
if ( Flags & ( PRINTER_CHANGE_FORM | PRINTER_CHANGE_ADD_PRINTER_DRIVER ) ) {
|
|
|
|
for ( mypIniPrinter = pIniSpooler->pIniPrinter;
|
|
mypIniPrinter != NULL ;
|
|
mypIniPrinter = mypIniPrinter->pNext ) {
|
|
|
|
UpdatePrinterIni ( mypIniPrinter, CHANGEID_ONLY );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( pSpool = pIniSpooler->pSpool ) {
|
|
|
|
DBGMSG( DBG_NOTIFY, ("SetPrinterChange %ws; Flags: %08x\n",
|
|
pIniSpooler->pMachineName, Flags));
|
|
|
|
for ( ; pSpool; pSpool = pSpool->pNext) {
|
|
|
|
//
|
|
// Only send the notification to the user if the printer is visible.
|
|
//
|
|
if (PrinterNotificationVisible(pIniPrinter, pSpool)) {
|
|
|
|
SetSpoolChange( pSpool,
|
|
aNotifyRawData,
|
|
pdwNotifyVectors,
|
|
Flags );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
LocalFindFirstPrinterChangeNotification(
|
|
HANDLE hPrinter,
|
|
DWORD fdwFilterFlags,
|
|
DWORD fdwOptions,
|
|
HANDLE hNotify,
|
|
PDWORD pfdwStatus,
|
|
PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
|
|
PVOID pvReserved1)
|
|
{
|
|
PINIPRINTER pIniPrinter = NULL;
|
|
PSPOOL pSpool = (PSPOOL)hPrinter;
|
|
|
|
EnterSplSem();
|
|
|
|
switch (ValidateStartNotify(pSpool,
|
|
fdwFilterFlags,
|
|
fdwOptions,
|
|
pPrinterNotifyOptions,
|
|
&pIniPrinter)) {
|
|
case STATUS_PORT:
|
|
|
|
DBGMSG(DBG_NOTIFY, ("LFFPCN: Port nomon 0x%x\n", pSpool));
|
|
pSpool->eStatus |= STATUS_PORT;
|
|
|
|
LeaveSplSem();
|
|
|
|
*pfdwStatus = 0;
|
|
|
|
return ProvidorFindFirstPrinterChangeNotification(pSpool->hPort,
|
|
fdwFilterFlags,
|
|
fdwOptions,
|
|
hNotify,
|
|
pPrinterNotifyOptions,
|
|
pvReserved1);
|
|
case STATUS_FAIL:
|
|
|
|
DBGMSG(DBG_WARNING, ("ValidateStartNotify failed!\n"));
|
|
LeaveSplSem();
|
|
return FALSE;
|
|
|
|
case STATUS_VALID:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get any other handle state we need into this notification handle. This
|
|
// operation is guaranteed to be stateless.
|
|
//
|
|
if (!GetClientTokenForNotification(pSpool)) {
|
|
return FALSE;
|
|
}
|
|
|
|
pSpool->eStatus = STATUS_NULL;
|
|
|
|
if (pPrinterNotifyOptions) {
|
|
|
|
if (!SetupNotifyOptions(pSpool, pPrinterNotifyOptions)) {
|
|
|
|
DBGMSG(DBG_WARNING, ("SetupNotifyOptions failed!\n"));
|
|
LeaveSplSem();
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Setup notification
|
|
//
|
|
DBGMSG(DBG_NOTIFY, ("LFFPCN: Port has monitor: Setup 0x%x\n", pSpool));
|
|
|
|
pSpool->WaitFlags = fdwFilterFlags;
|
|
pSpool->hNotify = hNotify;
|
|
pSpool->eStatus |= STATUS_VALID;
|
|
|
|
pSpool->Status |= SPOOL_STATUS_NOTIFY;
|
|
|
|
LeaveSplSem();
|
|
|
|
*pfdwStatus = PRINTER_NOTIFY_STATUS_ENDPOINT;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
LocalFindClosePrinterChangeNotification(
|
|
HANDLE hPrinter)
|
|
{
|
|
PSPOOL pSpool = (PSPOOL)hPrinter;
|
|
BOOL bReturn = FALSE;
|
|
|
|
|
|
if (ValidateSpoolHandle(pSpool, 0)) {
|
|
|
|
EnterSplSem();
|
|
|
|
//
|
|
// If it's the port case (false connect) we pass the close
|
|
// request to the right providor.
|
|
// Otherwise, close ourselves.
|
|
//
|
|
if (pSpool->eStatus & STATUS_PORT) {
|
|
|
|
DBGMSG(DBG_TRACE, ("LFCPCN: Port nomon 0x%x\n", pSpool));
|
|
|
|
LeaveSplSem();
|
|
|
|
bReturn = ProvidorFindClosePrinterChangeNotification(pSpool->hPort);
|
|
|
|
} else {
|
|
|
|
if (pSpool->eStatus & STATUS_VALID) {
|
|
|
|
DBGMSG(DBG_TRACE, ("LFCPCN: Close notify 0x%x\n", pSpool));
|
|
|
|
pSpool->WaitFlags = 0;
|
|
pSpool->eStatus = STATUS_NULL;
|
|
|
|
pSpool->Status &= ~SPOOL_STATUS_NOTIFY;
|
|
|
|
bReturn = TRUE;
|
|
|
|
} else {
|
|
|
|
DBGMSG(DBG_WARNING, ("LFCPCN: Invalid handle 0x%x\n", pSpool));
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
LeaveSplSem();
|
|
}
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
|
|
ESTATUS
|
|
ValidateStartNotify(
|
|
PSPOOL pSpool,
|
|
DWORD fdwFilterFlags,
|
|
DWORD fdwOptions,
|
|
PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
|
|
PINIPRINTER* ppIniPrinter)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Validates the pSpool and Flags for notifications.
|
|
|
|
Arguments:
|
|
|
|
pSpool - pSpool to validate
|
|
|
|
fdwFilterFlags - Flags to validate
|
|
|
|
fdwOptions - Options to validate
|
|
|
|
pPrinterNotifyOptions
|
|
|
|
ppIniPrinter - returned pIniPrinter; valid only STATUS_VALID
|
|
|
|
Return Value:
|
|
|
|
EWAITSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PINIPORT pIniPort;
|
|
|
|
if (ValidateSpoolHandle(pSpool, 0)) {
|
|
|
|
if ( pSpool->TypeofHandle & PRINTER_HANDLE_PRINTER ) {
|
|
|
|
*ppIniPrinter = pSpool->pIniPrinter;
|
|
|
|
} else if (pSpool->TypeofHandle & PRINTER_HANDLE_SERVER) {
|
|
|
|
*ppIniPrinter = NULL;
|
|
|
|
} else if ((pSpool->TypeofHandle & PRINTER_HANDLE_PORT) &&
|
|
(pIniPort = pSpool->pIniPort) &&
|
|
(pIniPort->signature == IPO_SIGNATURE) &&
|
|
!(pSpool->pIniPort->Status & PP_MONITOR)) {
|
|
|
|
if (pSpool->hPort == INVALID_PORT_HANDLE) {
|
|
|
|
DBGMSG(DBG_WARNING, ("WaitForPrinterChange called for invalid port handle. Setting last error to %d\n",
|
|
pSpool->OpenPortError));
|
|
|
|
SetLastError(pSpool->OpenPortError);
|
|
return STATUS_FAIL;
|
|
}
|
|
|
|
return STATUS_PORT;
|
|
|
|
} else {
|
|
|
|
DBGMSG(DBG_WARNING, ("The handle is invalid\n"));
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return STATUS_FAIL;
|
|
}
|
|
} else {
|
|
|
|
*ppIniPrinter = NULL;
|
|
}
|
|
|
|
//
|
|
// Allow only one wait on each handle.
|
|
//
|
|
if( pSpool->Status & SPOOL_STATUS_NOTIFY ) {
|
|
|
|
DBGMSG(DBG_WARNING, ("There is already a thread waiting on this handle\n"));
|
|
SetLastError(ERROR_ALREADY_WAITING);
|
|
|
|
return STATUS_FAIL;
|
|
}
|
|
|
|
if (!(fdwFilterFlags & PRINTER_CHANGE_VALID) && !pPrinterNotifyOptions) {
|
|
|
|
DBGMSG(DBG_WARNING, ("The wait flags specified are invalid\n"));
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return STATUS_FAIL;
|
|
}
|
|
|
|
return STATUS_VALID;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
VOID
|
|
GetInfoData(
|
|
PSPOOL pSpool,
|
|
PNOTIFY_RAW_DATA pNotifyRawData,
|
|
PNOTIFY_FIELD_TYPE pNotifyFieldType,
|
|
PPRINTER_NOTIFY_INFO_DATA pData,
|
|
PBYTE* ppBuffer)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Based on the type and field, find and add the information.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
static LPWSTR szNULL = L"";
|
|
DWORD cbData = 0;
|
|
DWORD cbNeeded = 0;
|
|
|
|
union {
|
|
DWORD dwData;
|
|
PDWORD pdwData;
|
|
PWSTR pszData;
|
|
PVOID pvData;
|
|
PINIJOB pIniJob;
|
|
PINIPORT pIniPort;
|
|
PDEVMODE pDevMode;
|
|
PSECURITY_DESCRIPTOR pSecurityDescriptor;
|
|
PINIPRINTER pIniPrinter;
|
|
|
|
PWSTR* ppszData;
|
|
PINIPORT* ppIniPort;
|
|
PINIPRINTER* ppIniPrinter;
|
|
PINIDRIVER* ppIniDriver;
|
|
PINIPRINTPROC* ppIniPrintProc;
|
|
LPDEVMODE* ppDevMode;
|
|
PSECURITY_DESCRIPTOR* ppSecurityDescriptor;
|
|
} Var;
|
|
|
|
Var.pvData = (PBYTE)pNotifyRawData->pvData + pNotifyFieldType->Offset;
|
|
*ppBuffer = NULL;
|
|
|
|
//
|
|
// Determine space needed, and convert Data from an offset into the
|
|
// actual data.
|
|
//
|
|
switch (pNotifyFieldType->Table) {
|
|
case TABLE_JOB_POSITION:
|
|
|
|
FindJob(Var.pIniJob->pIniPrinter,
|
|
Var.pIniJob->JobId,
|
|
&Var.dwData);
|
|
goto DoDWord;
|
|
|
|
case TABLE_JOB_STATUS:
|
|
|
|
Var.dwData = MapJobStatus(MAP_READABLE, *Var.pdwData);
|
|
goto DoDWord;
|
|
|
|
case TABLE_DWORD:
|
|
|
|
Var.dwData = *Var.pdwData;
|
|
goto DoDWord;
|
|
|
|
case TABLE_DEVMODE:
|
|
|
|
Var.pDevMode = *Var.ppDevMode;
|
|
|
|
if (Var.pDevMode) {
|
|
|
|
cbData = Var.pDevMode->dmSize + Var.pDevMode->dmDriverExtra;
|
|
|
|
} else {
|
|
|
|
cbData = 0;
|
|
}
|
|
|
|
break;
|
|
|
|
case TABLE_SECURITYDESCRIPTOR:
|
|
|
|
Var.pSecurityDescriptor = *Var.ppSecurityDescriptor;
|
|
cbData = GetSecurityDescriptorLength(Var.pSecurityDescriptor);
|
|
break;
|
|
|
|
case TABLE_STRING:
|
|
|
|
Var.pszData = *Var.ppszData;
|
|
goto DoString;
|
|
|
|
case TABLE_TIME:
|
|
|
|
//
|
|
// Var already points to the SystemTime.
|
|
//
|
|
cbData = sizeof(SYSTEMTIME);
|
|
break;
|
|
|
|
case TABLE_PRINTPROC:
|
|
|
|
Var.pszData = (*Var.ppIniPrintProc)->pName;
|
|
goto DoString;
|
|
|
|
case TABLE_JOB_PRINTERNAME:
|
|
|
|
Var.pszData = (*Var.ppIniPrinter)->pName;
|
|
goto DoString;
|
|
|
|
case TABLE_JOB_PORT:
|
|
|
|
Var.pIniPort = *Var.ppIniPort;
|
|
|
|
//
|
|
// Only if the job has been scheduled will pIniJob->pIniPort be
|
|
// valid. If it is NULL, then just call DoString which will
|
|
// return a NULL string.
|
|
//
|
|
if (Var.pIniPort) {
|
|
|
|
Var.pszData = Var.pIniPort->pName;
|
|
}
|
|
goto DoString;
|
|
|
|
case TABLE_DRIVER:
|
|
|
|
Var.pszData = (*Var.ppIniDriver)->pName;
|
|
goto DoString;
|
|
|
|
case TABLE_PRINTER_SERVERNAME:
|
|
|
|
Var.pszData = pSpool->pFullMachineName;
|
|
goto DoString;
|
|
|
|
case TABLE_PRINTER_STATUS:
|
|
|
|
Var.dwData = MapPrinterStatus(MAP_READABLE, Var.pIniPrinter->Status) |
|
|
Var.pIniPrinter->PortStatus;
|
|
goto DoDWord;
|
|
|
|
case TABLE_PRINTER_PORT:
|
|
|
|
// Get required printer port size
|
|
cbNeeded = 0;
|
|
GetPrinterPorts(Var.pIniPrinter, 0, &cbNeeded);
|
|
|
|
*ppBuffer = AllocSplMem(cbNeeded);
|
|
|
|
if (*ppBuffer)
|
|
GetPrinterPorts(Var.pIniPrinter, (LPWSTR) *ppBuffer, &cbNeeded);
|
|
|
|
Var.pszData = (LPWSTR) *ppBuffer;
|
|
|
|
goto DoString;
|
|
|
|
case TABLE_NULLSTRING:
|
|
|
|
Var.pszData = NULL;
|
|
goto DoString;
|
|
|
|
case TABLE_ZERO:
|
|
|
|
Var.dwData = 0;
|
|
goto DoDWord;
|
|
|
|
default:
|
|
SPLASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
pData->NotifyData.Data.pBuf = Var.pvData;
|
|
pData->NotifyData.Data.cbBuf = cbData;
|
|
|
|
return;
|
|
|
|
|
|
DoDWord:
|
|
pData->NotifyData.adwData[0] = Var.dwData;
|
|
pData->NotifyData.adwData[1] = 0;
|
|
return;
|
|
|
|
DoString:
|
|
if (Var.pszData) {
|
|
|
|
//
|
|
// Calculate string length.
|
|
//
|
|
pData->NotifyData.Data.cbBuf = (wcslen(Var.pszData)+1) *
|
|
sizeof(Var.pszData[0]);
|
|
|
|
pData->NotifyData.Data.pBuf = Var.pszData;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Use NULL string.
|
|
//
|
|
pData->NotifyData.Data.cbBuf = sizeof(Var.pszData[0]);
|
|
pData->NotifyData.Data.pBuf = szNULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
|
|
|
VOID
|
|
NotifyInfoTypes(
|
|
PSPOOL pSpool,
|
|
PNOTIFY_RAW_DATA pNotifyRawData,
|
|
PDWORD pdwNotifyVectors,
|
|
DWORD ChangeFlags)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends notification info (possibly with PRINTER_NOTIFY_INFO) to
|
|
the router.
|
|
|
|
Arguments:
|
|
|
|
pSpool -- Handle the notification is occurring on.
|
|
|
|
pNotifyRawData -- Array of size NOTIFY_TYPE_MAX that has the
|
|
offset structure can be used against + id.
|
|
|
|
pdwNotifyVectors -- Identifies what's changing (# elements
|
|
is also NOTIFY_TYPE_MAX).
|
|
|
|
NULL if no changes needed.
|
|
|
|
ChangeFlags -- Old style change flags.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PNOTIFY_FIELD_TYPE pNotifyFieldType;
|
|
PRINTER_NOTIFY_INFO_DATA Data;
|
|
PBYTE pBuffer;
|
|
BOOL bReturn;
|
|
|
|
DWORD i,j;
|
|
DWORD dwMask;
|
|
|
|
//
|
|
// If we are not valid, OR
|
|
// we have no notify vectors, OR
|
|
// we have no RAW data OR
|
|
// our vectors don't match what change
|
|
// then
|
|
// If no ChangeFlags return
|
|
// DoReply and avoid any Partials.
|
|
//
|
|
if (!(pSpool->eStatus & STATUS_INFO) ||
|
|
!pdwNotifyVectors ||
|
|
!pNotifyRawData ||
|
|
(!(pdwNotifyVectors[0] & pSpool->adwNotifyVectors[0] ||
|
|
pdwNotifyVectors[1] & pSpool->adwNotifyVectors[1]))) {
|
|
|
|
if (!ChangeFlags)
|
|
return;
|
|
|
|
goto DoReply;
|
|
}
|
|
|
|
//
|
|
// HACK: Special case NVPurge so that it causes a discard.
|
|
// (We don't want to send all those notifications.)
|
|
//
|
|
if (pdwNotifyVectors == NVPurge) {
|
|
|
|
PartialReplyPrinterChangeNotification(pSpool->hNotify, NULL);
|
|
goto DoReply;
|
|
}
|
|
|
|
for (i=0; i< NOTIFY_TYPE_MAX; i++, pdwNotifyVectors++) {
|
|
|
|
dwMask = 0x1;
|
|
|
|
SPLASSERT(adwNotifyFieldOffsets[i] < sizeof(DWORD)*8);
|
|
|
|
for (j=0; j< adwNotifyFieldOffsets[i]; j++, dwMask <<= 1) {
|
|
|
|
//
|
|
// If we have a change we are interested in,
|
|
// PartialReply.
|
|
//
|
|
if (dwMask & *pdwNotifyVectors & pSpool->adwNotifyVectors[i]) {
|
|
|
|
pNotifyFieldType = &apNotifyFieldTypes[i][j];
|
|
|
|
GetInfoData(pSpool,
|
|
&pNotifyRawData[i],
|
|
pNotifyFieldType,
|
|
&Data,
|
|
&pBuffer);
|
|
|
|
Data.Type = (WORD)i;
|
|
Data.Field = pNotifyFieldType->Field;
|
|
Data.Reserved = 0;
|
|
Data.Id = pNotifyRawData[i].dwId;
|
|
|
|
//
|
|
// If the partial reply failed, then we will be refreshing
|
|
// soon, so exit now.
|
|
//
|
|
bReturn = PartialReplyPrinterChangeNotification(
|
|
pSpool->hNotify,
|
|
&Data);
|
|
|
|
if (pBuffer) {
|
|
FreeSplMem(pBuffer);
|
|
}
|
|
|
|
if (!bReturn) {
|
|
|
|
DBGMSG(DBG_TRACE, ("PartialReplyPCN %x failed: %d!\n",
|
|
pSpool->hNotify,
|
|
GetLastError()));
|
|
goto DoReply;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DoReply:
|
|
|
|
//
|
|
// A full reply is needed to kick off the notification.
|
|
//
|
|
ReplyPrinterChangeNotification(pSpool->hNotify,
|
|
ChangeFlags,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
BOOL
|
|
RefreshBuildInfoData(
|
|
PSPOOL pSpool,
|
|
PPRINTER_NOTIFY_INFO pInfo,
|
|
UINT cInfo,
|
|
WORD Type,
|
|
PNOTIFY_RAW_DATA pNotifyRawData)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends notification info (possibly with PRINTER_NOTIFY_INFO) to
|
|
the router.
|
|
|
|
Arguments:
|
|
|
|
pSpool -- Handle the notification is occurring on.
|
|
|
|
pInfo -- Array of structure to receive new info.
|
|
|
|
cInfo -- Number of structures in array pInfo.
|
|
|
|
Type -- Indicates type of notification: job or printer.
|
|
|
|
pNotifyRawData -- Array of size NOTIFY_TYPE_MAX that has the
|
|
offset structure can be used against + id.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PRINTER_NOTIFY_INFO_DATA Data;
|
|
DWORD cbData;
|
|
PNOTIFY_FIELD_TYPE pNotifyFieldType;
|
|
PBYTE pBuffer;
|
|
BOOL bReturn;
|
|
|
|
DWORD j;
|
|
DWORD dwMask;
|
|
|
|
dwMask = 0x1;
|
|
|
|
SPLASSERT(adwNotifyFieldOffsets[Type] < sizeof(DWORD)*8);
|
|
|
|
for (j=0; j< adwNotifyFieldOffsets[Type]; j++, dwMask <<= 1) {
|
|
|
|
//
|
|
// If we have a change we are interested in,
|
|
// add it.
|
|
//
|
|
if (dwMask & pSpool->adwNotifyVectors[Type]) {
|
|
|
|
//
|
|
// Check if we have enough space.
|
|
//
|
|
if (pInfo->Count >= cInfo) {
|
|
SPLASSERT(pInfo->Count < cInfo);
|
|
return FALSE;
|
|
}
|
|
|
|
pNotifyFieldType = &apNotifyFieldTypes[Type][j];
|
|
|
|
GetInfoData(pSpool,
|
|
pNotifyRawData,
|
|
pNotifyFieldType,
|
|
&Data,
|
|
&pBuffer);
|
|
|
|
Data.Type = Type;
|
|
Data.Field = pNotifyFieldType->Field;
|
|
Data.Reserved = 0;
|
|
Data.Id = pNotifyRawData->dwId;
|
|
|
|
bReturn = AppendPrinterNotifyInfoData(pInfo, &Data, 0);
|
|
|
|
if (pBuffer)
|
|
FreeSplMem(pBuffer);
|
|
|
|
if (!bReturn) {
|
|
|
|
DBGMSG(DBG_WARNING, ("AppendPrinterNotifyInfoData failed: %d!\n",
|
|
GetLastError()));
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
BOOL
|
|
SetupNotifyVector(
|
|
PDWORD pdwNotifyVectors,
|
|
PPRINTER_NOTIFY_OPTIONS_TYPE pType)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Setup the notification vector based on pPrinterNotifyType.
|
|
We assume that the size of pPrinterNotifyType has been validated
|
|
(so that it fits within the containing structure). We only
|
|
need to verify that the Count falls within its stated Size.
|
|
|
|
Arguments:
|
|
|
|
pdwNotifyVectors - Structure to fill in.
|
|
|
|
pType - Source information.
|
|
|
|
Return Value:
|
|
|
|
TRUE = success,
|
|
FALSE = failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
PNOTIFY_FIELD_TYPE pNotifyFieldType;
|
|
PWORD pFields;
|
|
DWORD i, j;
|
|
DWORD Count;
|
|
BOOL bReturn = FALSE;
|
|
|
|
__try {
|
|
|
|
if( pType ){
|
|
|
|
Count = pType->Count;
|
|
pFields = pType->pFields;
|
|
|
|
if (pType->Type >= NOTIFY_TYPE_MAX) {
|
|
|
|
DBGMSG(DBG_WARN, ("SetupNotifyVector: type %d field %d not found!\n",
|
|
pType->Type, *pFields));
|
|
} else {
|
|
|
|
for (i=0; i < Count; i++, pFields++) {
|
|
|
|
if (*pFields >= adwNotifyFieldOffsets[pType->Type]) {
|
|
|
|
DBGMSG(DBG_WARN, ("SetupNotifyVector: type %d field %d not found!\n",
|
|
pType->Type, *pFields));
|
|
|
|
break;
|
|
}
|
|
|
|
SPLASSERT(apNotifyFieldTypes[pType->Type][*pFields].Table != TABLE_SPECIAL);
|
|
SPLASSERT(apNotifyFieldTypes[pType->Type][*pFields].Field == *pFields);
|
|
SPLASSERT(*pFields < 32);
|
|
|
|
//
|
|
// Found index j, set this bit in our array.
|
|
//
|
|
pdwNotifyVectors[pType->Type] |= (1 << *pFields);
|
|
}
|
|
|
|
if( i == Count ){
|
|
bReturn = TRUE;
|
|
}
|
|
}
|
|
}
|
|
} __except( EXCEPTION_EXECUTE_HANDLER ){
|
|
}
|
|
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
SetupNotifyOptions(
|
|
PSPOOL pSpool,
|
|
PPRINTER_NOTIFY_OPTIONS pOptions)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes pSpool->adwNotifyVectors.
|
|
|
|
Arguments:
|
|
|
|
pSpool - Spool handle to setup the notification against.
|
|
|
|
pOptions - Options that specify the notification.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success, FALSE - FAILURE
|
|
|
|
LastError set.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i;
|
|
BOOL bAccessGranted = TRUE;
|
|
|
|
SplInSem();
|
|
|
|
ZeroMemory(pSpool->adwNotifyVectors, sizeof(pSpool->adwNotifyVectors));
|
|
|
|
//
|
|
// Traverse Options structure.
|
|
//
|
|
for (i = 0; i < pOptions->Count; i++) {
|
|
|
|
if (!SetupNotifyVector(pSpool->adwNotifyVectors,
|
|
&pOptions->pTypes[i])){
|
|
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now check if we have sufficient privilege to setup the notification.
|
|
//
|
|
|
|
//
|
|
// Check if we are looking for the security descriptor on
|
|
// a printer. If so, we need READ_CONTROL or ACCESS_SYSTEM_SECURITY
|
|
// enabled.
|
|
//
|
|
if( pSpool->adwNotifyVectors[PRINTER_NOTIFY_TYPE] &
|
|
BIT(I_PRINTER_SECURITY_DESCRIPTOR )){
|
|
|
|
if( !AreAnyAccessesGranted( pSpool->GrantedAccess,
|
|
READ_CONTROL | ACCESS_SYSTEM_SECURITY )){
|
|
bAccessGranted = FALSE;
|
|
}
|
|
}
|
|
|
|
if( pSpool->adwNotifyVectors[PRINTER_NOTIFY_TYPE] &
|
|
~BIT(I_PRINTER_SECURITY_DESCRIPTOR )){
|
|
|
|
if( pSpool->TypeofHandle & PRINTER_HANDLE_SERVER ){
|
|
|
|
//
|
|
// There does not appear to be a check for EnumPrinters.
|
|
//
|
|
// This seems odd since you there is a security check on a
|
|
// GetPrinter call, but there is none on EnumPrinters (aside
|
|
// from enumerating share printers only for remote non-admins).
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// This matches the check in SplGetPrinter: we need to
|
|
// have PRINTER_ACCESS_USE to read the non-security information.
|
|
//
|
|
if( !AccessGranted( SPOOLER_OBJECT_PRINTER,
|
|
PRINTER_ACCESS_USE,
|
|
pSpool )){
|
|
|
|
bAccessGranted = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !bAccessGranted ){
|
|
|
|
SetLastError( ERROR_ACCESS_DENIED );
|
|
return FALSE;
|
|
}
|
|
|
|
pSpool->eStatus |= STATUS_INFO;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
UINT
|
|
PopCount(
|
|
DWORD dwValue)
|
|
{
|
|
UINT i;
|
|
UINT cPopCount = 0;
|
|
|
|
for(i=0; i< sizeof(dwValue)*8; i++) {
|
|
|
|
if (dwValue & (1<<i))
|
|
cPopCount++;
|
|
}
|
|
|
|
return cPopCount;
|
|
}
|
|
|
|
|
|
BOOL
|
|
LocalRefreshPrinterChangeNotification(
|
|
HANDLE hPrinter,
|
|
DWORD dwColor,
|
|
PPRINTER_NOTIFY_OPTIONS pOptions,
|
|
PPRINTER_NOTIFY_INFO* ppInfo)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Refreshes data in the case of overflows.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PINIJOB pIniJob;
|
|
PINIPRINTER pIniPrinter;
|
|
DWORD cPrinters;
|
|
PSPOOL pSpool = (PSPOOL)hPrinter;
|
|
PDWORD pdwNotifyVectors = pSpool->adwNotifyVectors;
|
|
UINT cInfo = 0;
|
|
PPRINTER_NOTIFY_INFO pInfo = NULL;
|
|
NOTIFY_RAW_DATA NotifyRawData;
|
|
|
|
EnterSplSem();
|
|
|
|
if (!ValidateSpoolHandle(pSpool, 0 ) ||
|
|
!(pSpool->eStatus & STATUS_INFO)) {
|
|
|
|
SetLastError( ERROR_INVALID_HANDLE );
|
|
goto Fail;
|
|
}
|
|
|
|
//
|
|
// New bits added, can't compare directly against PRINTER_HANDLE_SERVER.
|
|
//
|
|
if( pSpool->TypeofHandle & PRINTER_HANDLE_SERVER ){
|
|
|
|
//
|
|
// If the call is a remote one, and the user is not an admin, then
|
|
// we don't want to show unshared printers.
|
|
//
|
|
BOOL bHideUnshared = (pSpool->TypeofHandle & PRINTER_HANDLE_REMOTE_CALL) &&
|
|
!(pSpool->TypeofHandle & PRINTER_HANDLE_REMOTE_ADMIN);
|
|
|
|
|
|
for (cPrinters = 0, pIniPrinter = pSpool->pIniSpooler->pIniPrinter;
|
|
pIniPrinter;
|
|
pIniPrinter=pIniPrinter->pNext ) {
|
|
|
|
if ((!(pIniPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED) &&
|
|
bHideUnshared)
|
|
|| !ShowThisPrinter(pIniPrinter, NULL)
|
|
) {
|
|
|
|
continue;
|
|
}
|
|
|
|
cPrinters++;
|
|
}
|
|
|
|
cInfo += PopCount(pSpool->adwNotifyVectors[PRINTER_NOTIFY_TYPE]) *
|
|
cPrinters;
|
|
|
|
//
|
|
// Traverse all printers and create info.
|
|
//
|
|
pInfo = RouterAllocPrinterNotifyInfo(cInfo);
|
|
|
|
if (!pInfo)
|
|
goto Fail;
|
|
|
|
if (pSpool->adwNotifyVectors[PRINTER_NOTIFY_TYPE]) {
|
|
|
|
for (pIniPrinter = pSpool->pIniSpooler->pIniPrinter;
|
|
pIniPrinter;
|
|
pIniPrinter=pIniPrinter->pNext) {
|
|
|
|
//
|
|
// Do not send notification for non-shared printers for remote
|
|
// users who are not admins
|
|
//
|
|
if ((!(pIniPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED) &&
|
|
bHideUnshared )
|
|
|| !ShowThisPrinter(pIniPrinter, NULL)
|
|
) {
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
|
|
NotifyRawData.pvData = pIniPrinter;
|
|
NotifyRawData.dwId = pIniPrinter->dwUniqueSessionID;
|
|
|
|
if (!RefreshBuildInfoData(pSpool,
|
|
pInfo,
|
|
cInfo,
|
|
PRINTER_NOTIFY_TYPE,
|
|
&NotifyRawData)) {
|
|
|
|
goto Fail;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Calculate size of buffer needed.
|
|
//
|
|
if (pSpool->adwNotifyVectors[PRINTER_NOTIFY_TYPE]) {
|
|
|
|
//
|
|
// Setup printer info.
|
|
//
|
|
cInfo += PopCount(pSpool->adwNotifyVectors[PRINTER_NOTIFY_TYPE]);
|
|
}
|
|
|
|
if (pSpool->adwNotifyVectors[JOB_NOTIFY_TYPE]) {
|
|
|
|
cInfo += PopCount(pSpool->adwNotifyVectors[JOB_NOTIFY_TYPE]) *
|
|
pSpool->pIniPrinter->cJobs;
|
|
}
|
|
|
|
//
|
|
// Traverse all jobs and create info.
|
|
//
|
|
pInfo = RouterAllocPrinterNotifyInfo(cInfo);
|
|
|
|
if (!pInfo)
|
|
goto Fail;
|
|
|
|
if (pSpool->adwNotifyVectors[PRINTER_NOTIFY_TYPE]) {
|
|
|
|
NotifyRawData.pvData = pSpool->pIniPrinter;
|
|
NotifyRawData.dwId = pSpool->pIniPrinter->dwUniqueSessionID;
|
|
|
|
if (!RefreshBuildInfoData(pSpool,
|
|
pInfo,
|
|
cInfo,
|
|
PRINTER_NOTIFY_TYPE,
|
|
&NotifyRawData)) {
|
|
|
|
goto Fail;
|
|
}
|
|
}
|
|
|
|
if (pSpool->adwNotifyVectors[JOB_NOTIFY_TYPE]) {
|
|
|
|
for (pIniJob = pSpool->pIniPrinter->pIniFirstJob;
|
|
pIniJob;
|
|
pIniJob = pIniJob->pIniNextJob) {
|
|
|
|
//
|
|
// Hide Chained Jobs
|
|
//
|
|
|
|
if (!(pIniJob->Status & JOB_HIDDEN )) {
|
|
|
|
NotifyRawData.pvData = pIniJob;
|
|
NotifyRawData.dwId = pIniJob->JobId;
|
|
|
|
if (!RefreshBuildInfoData(pSpool,
|
|
pInfo,
|
|
cInfo,
|
|
JOB_NOTIFY_TYPE,
|
|
&NotifyRawData)) {
|
|
|
|
goto Fail;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SPLASSERT(cInfo >= pInfo->Count);
|
|
LeaveSplSem();
|
|
|
|
*ppInfo = pInfo;
|
|
return TRUE;
|
|
|
|
Fail:
|
|
|
|
SPLASSERT(!pInfo || cInfo >= pInfo->Count);
|
|
LeaveSplSem();
|
|
|
|
*ppInfo = NULL;
|
|
if (pInfo) {
|
|
RouterFreePrinterNotifyInfo(pInfo);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
GetClientTokenForNotification
|
|
|
|
Routine Description:
|
|
|
|
Populate the client handle with the callers token if this is a server handle
|
|
(printer handles have implicitely been access checked by virtue of the fact
|
|
that you could open it).
|
|
|
|
Arguments:
|
|
|
|
pSpool - The printer handle that we are populating with the token
|
|
information
|
|
|
|
Return Value:
|
|
|
|
TRUE if we could in fact return the token.
|
|
|
|
--*/
|
|
BOOL
|
|
GetClientTokenForNotification(
|
|
IN OUT SPOOL *pSpool
|
|
)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
|
|
if (!pSpool)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
|
|
bRet = FALSE;
|
|
}
|
|
|
|
if (bRet)
|
|
{
|
|
//
|
|
// If this is a printer handle, then we need to get the client token handle
|
|
//
|
|
if (pSpool->TypeofHandle & PRINTER_HANDLE_SERVER)
|
|
{
|
|
//
|
|
// Only get the new client token if we don't have one already.
|
|
//
|
|
if (!pSpool->hClientToken)
|
|
{
|
|
bRet = GetTokenHandle(&pSpool->hClientToken);
|
|
}
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|