windows-nt/Source/XPSP1/NT/printscan/print/spooler/spoolss/dll/info.c
2020-09-26 16:20:57 +08:00

814 lines
18 KiB
C

/*++
Copyright (c) 1994 Microsoft Corporation
All rights reserved
Module Name:
info.c
Abstract:
Handles marshalling support for notifications.
Author:
Environment:
User Mode -Win32
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#include <ntfytab.h>
#include <stddef.h>
#define PRINTER_STRINGS
#include <data.h>
#define DEFINE(field, attrib, table, x, y) { attrib, table },
NOTIFY_ATTRIB_TABLE NotifyAttribPrinter[] = {
#include "ntfyprn.h"
};
NOTIFY_ATTRIB_TABLE NotifyAttribJob[] = {
#include "ntfyjob.h"
};
#undef DEFINE
PNOTIFY_ATTRIB_TABLE pNotifyAttribTable[] = {
NotifyAttribPrinter,
NotifyAttribJob
};
DWORD adwNotifyAttribMax[] = {
COUNTOF(NotifyAttribPrinter),
COUNTOF(NotifyAttribJob)
};
DWORD adwNotifyDatatypes[] = NOTIFY_DATATYPES;
extern DWORD cDefaultPrinterNotifyInfoData;
//
// Forward prototypes.
//
PPRINTER_NOTIFY_INFO
RouterAllocPrinterNotifyInfo(
DWORD cPrinterNotifyInfoData)
{
PPRINTER_NOTIFY_INFO pPrinterNotifyInfo;
if (!cPrinterNotifyInfoData)
cPrinterNotifyInfoData = cDefaultPrinterNotifyInfoData;
pPrinterNotifyInfo = MIDL_user_allocate(
sizeof(*pPrinterNotifyInfo) -
sizeof(pPrinterNotifyInfo->aData) +
cPrinterNotifyInfoData *
sizeof(PRINTER_NOTIFY_INFO_DATA));
//
// Must initialize version/count.
//
if (pPrinterNotifyInfo != NULL) {
pPrinterNotifyInfo->Version = NOTIFICATION_VERSION;
pPrinterNotifyInfo->Count = 0;
if (pPrinterNotifyInfo) {
ClearPrinterNotifyInfo(pPrinterNotifyInfo, NULL);
}
}
return pPrinterNotifyInfo;
}
VOID
SetupPrinterNotifyInfo(
PPRINTER_NOTIFY_INFO pInfo,
PCHANGE pChange)
{
//
// Set the color flag.
//
pInfo->Flags |= PRINTER_NOTIFY_INFO_COLORSET;
//
// We should set the discard bit if there was a previous overflow
// and we didn't or couldn't allocate a pInfo structure.
// Do it now.
//
if (pChange && pChange->eStatus & STATUS_CHANGE_DISCARDED) {
pInfo->Flags |= PRINTER_NOTIFY_INFO_DISCARDED;
}
}
BOOL
FreePrinterNotifyInfoData(
PPRINTER_NOTIFY_INFO pInfo)
{
DWORD i;
PPRINTER_NOTIFY_INFO_DATA pData;
for(pData = pInfo->aData, i=pInfo->Count;
i;
pData++, i--) {
if ((TABLE)pData->Reserved != TABLE_DWORD) {
MIDL_user_free(pData->NotifyData.Data.pBuf);
}
}
return TRUE;
}
BOOL
RouterFreePrinterNotifyInfo(
PPRINTER_NOTIFY_INFO pInfo)
{
if (!pInfo) {
DBGMSG(DBG_WARNING, ("RFreePrinterNotifyInfo called with NULL!\n"));
return FALSE;
}
FreePrinterNotifyInfoData(pInfo);
MIDL_user_free(pInfo);
return TRUE;
}
VOID
ClearPrinterNotifyInfo(
PPRINTER_NOTIFY_INFO pInfo,
PCHANGE pChange)
/*++
Routine Description:
Clears the PRINTER_NOTIFY structure for more notifications.
Arguments:
pInfo - Info to clear ( pInfo->aData must be valid if pInfo->Count != 0 )
pChange - Associated pChange structure.
Return Value:
--*/
{
if (!pInfo)
return;
FreePrinterNotifyInfoData(pInfo);
pInfo->Flags = 0;
pInfo->Count = 0;
if (pChange)
SetupPrinterNotifyInfo(pInfo, pChange);
}
VOID
SetDiscardPrinterNotifyInfo(
PPRINTER_NOTIFY_INFO pInfo,
PCHANGE pChange)
{
if (pInfo) {
FreePrinterNotifyInfoData(pInfo);
pInfo->Count = 0;
pInfo->Flags |= PRINTER_NOTIFY_INFO_DISCARDED;
}
if (pChange)
pChange->eStatus |= STATUS_CHANGE_DISCARDED;
}
DWORD
AppendPrinterNotifyInfo(
PPRINTHANDLE pPrintHandle,
DWORD dwColor,
PPRINTER_NOTIFY_INFO pInfoSrc)
/*++
Routine Description:
Appends pInfoSrc to the pPrintHandle. We may get Src with zero
notifications. This occurs when when the user requests a refresh
and the client spooler synchronously replies back to clear out
everything.
Arguments:
pPrintHandle -- handle to update
pInfoSrc -- Source of info. NULL = no info.
Return Value:
Result of action.
--*/
{
PPRINTER_NOTIFY_INFO pInfoDest;
PCHANGE pChange;
PPRINTER_NOTIFY_INFO_DATA pDataSrc;
DWORD i;
pChange = pPrintHandle->pChange;
pInfoDest = pChange->ChangeInfo.pPrinterNotifyInfo;
//
// May be NULL if RPCing and the buffer is being used.
// The worker thread will free the data.
//
if (!pInfoDest) {
pInfoDest = RouterAllocPrinterNotifyInfo(0);
if (!pInfoDest) {
DBGMSG(DBG_WARNING,
("AppendPrinterNotifyInfo: Alloc fail, Can't set DISCARD\n"));
goto Discard;
}
SetupPrinterNotifyInfo(pInfoDest, pChange);
pChange->ChangeInfo.pPrinterNotifyInfo = pInfoDest;
}
if (!pInfoSrc) {
return 0;
}
//
// We must handle the case where the user requests and receives
// a refresh, but the there was an outstanding RPC that is just
// now being processed. In this case, we must drop this
// notification. We determine this by maintaining a color value.
//
// Note that we can't return the DISCARDNOTED flag, since this can't
// trigger an overflow.
//
// If (Color is set) AND (NOT color same) fail.
//
if (pInfoSrc->Flags & PRINTER_NOTIFY_INFO_COLORSET) {
if (dwColor != pChange->dwColor) {
DBGMSG(DBG_WARNING, ("AppendPrinterNotifyInfo: Color mismatch info %d != %d; discard\n",
dwColor, pChange->dwColor));
//
// Clear it out, and we're done.
//
ClearPrinterNotifyInfo(pInfoDest,
pChange);
return PRINTER_NOTIFY_INFO_COLORMISMATCH;
}
}
//
// OR in the flags.
//
pInfoDest->Flags |= pInfoSrc->Flags;
//
// Check for overflow.
//
if (pInfoSrc->Count + pInfoDest->Count < cDefaultPrinterNotifyInfoData) {
//
// Now copy everything over.
//
for (pDataSrc = pInfoSrc->aData, i = pInfoSrc->Count;
i;
i--, pDataSrc++) {
AppendPrinterNotifyInfoData(
pInfoDest,
pDataSrc,
PRINTER_NOTIFY_INFO_DATA_COMPACT);
}
} else {
Discard:
SetDiscardPrinterNotifyInfo(pInfoDest, pChange);
return PRINTER_NOTIFY_INFO_DISCARDED |
PRINTER_NOTIFY_INFO_DISCARDNOTED;
}
return 0;
}
BOOL
AppendPrinterNotifyInfoData(
PPRINTER_NOTIFY_INFO pInfoDest,
PPRINTER_NOTIFY_INFO_DATA pDataSrc,
DWORD fdwFlags)
/*++
Routine Description:
Append the data to the pInfoDest. If pDataSrc is NULL, set discard.
Arguments:
Return Value:
--*/
{
PPRINTER_NOTIFY_INFO_DATA pDataDest;
DWORD i;
BOOL bCompact = FALSE;
DWORD Count;
PPRINTER_NOTIFY_INFO_DATA pDataNew;
BOOL bNewSlot = TRUE;
DWORD Type;
DWORD Field;
DWORD Table;
DWORD Id;
EnterRouterSem();
if (!pDataSrc || (pInfoDest->Flags & PRINTER_NOTIFY_INFO_DISCARDED)) {
SetLastError(ERROR_OUT_OF_STRUCTURES);
goto DoDiscard;
}
Type = pDataSrc->Type;
Field = pDataSrc->Field;
Table = pDataSrc->Reserved;
Id = pDataSrc->Id;
//
// Validate this is a correct type.
//
if (Type < NOTIFY_TYPE_MAX && Field < adwNotifyAttribMax[Type]) {
//
// If Table == 0, then the caller did not specify the type,
// so we fill it in as appropriate.
//
// If it is non-zero and it's doesn't match our value, then
// return an error.
//
if (Table != pNotifyAttribTable[Type][Field].Table) {
if (Table) {
//
// The specified table type does not match our table
// type. Punt, since we can't marshall.
//
DBGMSG(DBG_WARNING, ("Table = %d, != Type %d /Field %d!\n",
Table, Field, Type));
SetLastError(ERROR_INVALID_PARAMETER);
goto DoDiscard;
}
//
// Fix up the Table entry.
//
Table = pNotifyAttribTable[Type][Field].Table;
pDataSrc->Reserved = (TABLE)Table;
}
bCompact = (fdwFlags & PRINTER_NOTIFY_INFO_DATA_COMPACT) &&
(pNotifyAttribTable[Type][Field].Attrib &
TABLE_ATTRIB_COMPACT);
} else {
//
// This is not an error case, since we can still marshall fields
// we don't know about as long as the Table is valid.
//
DBGMSG(DBG_WARNING, ("Unknown: Type= %d Field= %d!\n", Type, Field));
}
if (!Table || Table >= COUNTOF(adwNotifyDatatypes)) {
DBGMSG(DBG_WARNING, ("Table %d unknown; can't marshall!\n",
Table));
SetLastError(ERROR_INVALID_PARAMETER);
goto DoDiscard;
}
SPLASSERT(Table);
//
// Check if compactable.
//
if (bCompact) {
//
// We can compact, see if there is a match.
//
for (pDataDest = pInfoDest->aData, i = pInfoDest->Count;
i;
pDataDest++, i--) {
if (Type == pDataDest->Type &&
Field == pDataDest->Field &&
Id == pDataDest->Id) {
if (Table == TABLE_DWORD) {
pDataDest->NotifyData.adwData[0] =
pDataSrc->NotifyData.adwData[0];
pDataDest->NotifyData.adwData[1] =
pDataSrc->NotifyData.adwData[1];
goto Done;
}
//
// Must copy the data, so free the old one.
//
MIDL_user_free(pDataDest->NotifyData.Data.pBuf);
bNewSlot = FALSE;
break;
}
}
//
// pDataDest now points to the correct slot (either end or
// somewhere in the middle if we are compacting.
//
} else {
//
// Slot defaults to the end.
//
pDataDest = &pInfoDest->aData[pInfoDest->Count];
}
//
// Copy structure first
//
*pDataDest = *pDataSrc;
//
// The data may be either a pointer or the actual DWORD data.
//
if (adwNotifyDatatypes[Table] & TABLE_ATTRIB_DATA_PTR) {
DWORD cbData = pDataSrc->NotifyData.Data.cbBuf;
//
// Now copy everything over.
//
pDataNew = (PPRINTER_NOTIFY_INFO_DATA)MIDL_user_allocate(cbData);
if (!pDataNew) {
pDataDest->NotifyData.Data.pBuf = NULL;
DBGMSG( DBG_WARNING, ("Alloc %d bytes failed with %d\n",
GetLastError()));
goto DoDiscard;
}
CopyMemory(pDataNew, pDataSrc->NotifyData.Data.pBuf, cbData);
pDataDest->NotifyData.Data.cbBuf = cbData;
pDataDest->NotifyData.Data.pBuf = pDataNew;
}
//
// Increment if necessary.
//
if (bNewSlot)
pInfoDest->Count++;
Done:
LeaveRouterSem();
return TRUE;
DoDiscard:
SetDiscardPrinterNotifyInfo(pInfoDest, NULL);
LeaveRouterSem();
return FALSE;
}
BOOL
RouterRefreshPrinterChangeNotification(
HANDLE hPrinter,
DWORD dwColor,
PVOID pPrinterNotifyRefresh,
PPRINTER_NOTIFY_INFO* ppInfo)
/*++
Routine Description:
Implements the refresh portion of FindNextPrinterChangeNotification.
Arguments:
Return Value:
--*/
{
PPRINTHANDLE pPrintHandle = (PPRINTHANDLE)hPrinter;
BOOL bReturn;
EnterRouterSem();
if (!pPrintHandle ||
pPrintHandle->signature != PRINTHANDLE_SIGNATURE ||
!pPrintHandle->pChange ||
!(pPrintHandle->pChange->eStatus & (STATUS_CHANGE_VALID |
STATUS_CHANGE_INFO))) {
SetLastError(ERROR_INVALID_HANDLE);
goto Fail;
}
if (!pPrintHandle->pProvidor->PrintProvidor.fpRefreshPrinterChangeNotification) {
SetLastError(RPC_S_PROCNUM_OUT_OF_RANGE);
goto Fail;
}
pPrintHandle->pChange->dwColor = dwColor;
//
// Allow notifications to begin again.
//
pPrintHandle->pChange->eStatus &= ~(STATUS_CHANGE_DISCARDED |
STATUS_CHANGE_DISCARDNOTED);
ClearPrinterNotifyInfo(pPrintHandle->pChange->ChangeInfo.pPrinterNotifyInfo,
pPrintHandle->pChange);
LeaveRouterSem();
bReturn = (*pPrintHandle->pProvidor->PrintProvidor.fpRefreshPrinterChangeNotification)(
pPrintHandle->hPrinter,
pPrintHandle->pChange->dwColor,
pPrinterNotifyRefresh,
ppInfo);
//
// On failure, set discard.
//
if (!bReturn) {
EnterRouterSem();
//
// The handle should be valid since RPC guarentees that context
// handle access is serialized. However, a misbehaved
// multithreaded spooler component could cause this to happen.
//
SPLASSERT(pPrintHandle->signature == PRINTHANDLE_SIGNATURE);
if (pPrintHandle->pChange) {
//
// Disallow notifications since the refresh failed.
//
pPrintHandle->pChange->eStatus |= STATUS_CHANGE_DISCARDED;
}
LeaveRouterSem();
}
return bReturn;
Fail:
LeaveRouterSem();
return FALSE;
}
BOOL
NotifyNeeded(
PCHANGE pChange)
{
register PPRINTER_NOTIFY_INFO pInfo;
pInfo = pChange->ChangeInfo.pPrinterNotifyInfo;
if (pChange->eStatus & STATUS_CHANGE_DISCARDNOTED) {
return FALSE;
}
if (pChange->fdwChangeFlags || pChange->eStatus & STATUS_CHANGE_DISCARDED) {
return TRUE;
}
if (!pInfo) {
return FALSE;
}
if (pInfo->Flags & PRINTER_NOTIFY_INFO_DISCARDED || pInfo->Count) {
return TRUE;
}
return FALSE;
}
/*------------------------------------------------------------------------
Entry points for providors.
------------------------------------------------------------------------*/
BOOL
ReplyPrinterChangeNotification(
HANDLE hPrinter,
DWORD fdwChangeFlags,
PDWORD pdwResult,
PPRINTER_NOTIFY_INFO pPrinterNotifyInfo
)
/*++
Routine Description:
Updates a notification with an entire pPrinterNotifyInfo packet.
Providers can call PartialRPCN several times with small packets (the
router won't send them until ReplyPrinterChangeNotification is sent).
This allows batching and guarentees atmoic notifications.
Arguments:
hPrinter - Handle that is being watched.
fdwChangeFlags - Flags that changed (WPC type flags for compatibility)
pdwResult - Result of changes (OPTIONAL). Indicates whether changes
were discarded, and if the discard was noted.
pPrinterNotifyInfo - Information about change.
Return Value:
TRUE - success
FALSE - failure, call GetLastError().
--*/
{
return ReplyPrinterChangeNotificationWorker(
hPrinter,
0,
fdwChangeFlags,
pdwResult,
pPrinterNotifyInfo);
}
BOOL
PartialReplyPrinterChangeNotification(
HANDLE hPrinter,
PPRINTER_NOTIFY_INFO_DATA pDataSrc
)
/*++
Routine Description:
Updates the notify info without triggering a notification. This is
used to send multiple infos without rpcing on each one. Do a
ReplyPrinterChangeNotificiation at the end.
Arguments:
hPrinter -- Printer handle that changed.
pDataSrc -- Partial data to store. If NULL, indicates a discard
should be stored, causing the client to refresh.
Return Value:
--*/
{
LPPRINTHANDLE pPrintHandle = (LPPRINTHANDLE)hPrinter;
BOOL bReturn = FALSE;
PPRINTER_NOTIFY_INFO* ppInfoDest;
EnterRouterSem();
if (!pPrintHandle ||
pPrintHandle->signature != PRINTHANDLE_SIGNATURE ||
!pPrintHandle->pChange) {
DBGMSG(DBG_WARNING, ("PartialRPCN: Invalid handle 0x%x!\n",
hPrinter));
SetLastError(ERROR_INVALID_HANDLE);
goto Fail;
}
if (!(pPrintHandle->pChange->eStatus &
(STATUS_CHANGE_VALID|STATUS_CHANGE_INFO))) {
DBGMSG(DBG_WARNING, ("PartialRPCN: Invalid handle 0x%x state = 0x%x!\n",
hPrinter,
pPrintHandle->pChange->eStatus));
SetLastError(ERROR_INVALID_HANDLE);
goto Fail;
}
ppInfoDest = &pPrintHandle->pChange->ChangeInfo.pPrinterNotifyInfo;
if (!pDataSrc) {
bReturn = TRUE;
goto Discard;
}
//
// May be NULL if RPCing and the buffer is being used.
// The worker thread will free the data.
//
if (!*ppInfoDest) {
*ppInfoDest = RouterAllocPrinterNotifyInfo(0);
if (!*ppInfoDest) {
DBGMSG(DBG_WARNING,
("PartialReplyPCN: Alloc failed, discarding\n"));
//
// We should set the discard flag here, but we can't,
// so punt.
//
goto Discard;
}
SetupPrinterNotifyInfo(*ppInfoDest, pPrintHandle->pChange);
}
//
// Check that we have enough space for the current data.
//
if ((*ppInfoDest)->Count < cDefaultPrinterNotifyInfoData) {
bReturn = AppendPrinterNotifyInfoData(
*ppInfoDest,
pDataSrc,
PRINTER_NOTIFY_INFO_DATA_COMPACT);
} else {
SetLastError(ERROR_OUT_OF_STRUCTURES);
Discard:
SetDiscardPrinterNotifyInfo(*ppInfoDest, pPrintHandle->pChange);
}
Fail:
LeaveRouterSem();
return bReturn;
}