Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

6562 lines
166 KiB

/*++
Copyright (c) 1990 - 1995 Microsoft Corporation
Module Name:
util.c
Abstract:
This module provides all the utility functions for the Routing Layer and
the local Print Providor
Author:
Dave Snipp (DaveSn) 15-Mar-1991
Revision History:
Felix Maxa (amaxa) 18-Jun-2000
Added utility functions for cluster spoolers. Part of the DCR regarding
installation of printer drivers on cluster spoolers
Muhunthan Sivapragasam ( MuhuntS ) 5-June-1995
Moved from printer.c:
RegSetBinaryData
RegSetString
RegSetDWord
Wrote:
SameMultiSz
RegGetValue
Matthew A Felton ( MattFe ) 23-mar-1995
DeleteAllFilesAndDirectory
DeleteAllFilesInDirectory
CreateDirectoryWithoutImpersonatingUser
--*/
#include <precomp.h>
#pragma hdrstop
#include <winddiui.h>
#include <lm.h>
#include <aclapi.h>
#include <winsta.h>
#include "clusspl.h"
typedef LONG (WINAPI *pfnWinStationSendWindowMessage)(
HANDLE hServer,
ULONG sessionID,
ULONG timeOut,
ULONG hWnd,
ULONG Msg,
WPARAM wParam,
LPARAM lParam,
LONG *pResponse);
extern BOOL (*pfnOpenPrinter)(LPTSTR, LPHANDLE, LPPRINTER_DEFAULTS);
extern BOOL (*pfnClosePrinter)(HANDLE);
extern LONG (*pfnDocumentProperties)(HWND, HANDLE, LPWSTR, PDEVMODE, PDEVMODE, DWORD);
#define DEFAULT_MAX_TIMEOUT 300000 // 5 minute timeout.
CRITICAL_SECTION SpoolerSection;
PDBG_POINTERS gpDbgPointers = NULL;
pfnWinStationSendWindowMessage pfWinStationSendWindowMessage = NULL;
VOID
RunForEachSpooler(
HANDLE h,
PFNSPOOLER_MAP pfnMap
)
{
PINISPOOLER pIniSpooler;
PINISPOOLER pIniNextSpooler;
SplInSem();
if( pLocalIniSpooler ){
INCSPOOLERREF( pLocalIniSpooler );
for( pIniSpooler = pLocalIniSpooler; pIniSpooler; pIniSpooler = pIniNextSpooler ){
if( (*pfnMap)( h, pIniSpooler ) ){
pIniNextSpooler = pIniSpooler->pIniNextSpooler;
} else {
pIniNextSpooler = NULL;
}
if( pIniNextSpooler ){
INCSPOOLERREF( pIniNextSpooler );
}
DECSPOOLERREF( pIniSpooler );
}
}
}
VOID
RunForEachPrinter(
PINISPOOLER pIniSpooler,
HANDLE h,
PFNPRINTER_MAP pfnMap
)
{
PINIPRINTER pIniPrinter;
PINIPRINTER pIniNextPrinter;
SplInSem();
pIniPrinter = pIniSpooler->pIniPrinter;
if( pIniPrinter ){
INCPRINTERREF( pIniPrinter );
for( ; pIniPrinter; pIniPrinter = pIniNextPrinter ){
if( (*pfnMap)( h, pIniPrinter ) ){
pIniNextPrinter = pIniPrinter->pNext;
} else {
pIniNextPrinter = NULL;
}
if( pIniNextPrinter ){
INCPRINTERREF( pIniNextPrinter );
}
DECPRINTERREF( pIniPrinter );
DeletePrinterCheck( pIniPrinter );
}
}
}
#if DBG
HANDLE hcsSpoolerSection = NULL;
VOID
SplInSem(
VOID
)
{
if( hcsSpoolerSection ){
SPLASSERT( gpDbgPointers->pfnInsideCritSec( hcsSpoolerSection ));
} else {
SPLASSERT( SpoolerSection.OwningThread == (HANDLE)(ULONG_PTR)(GetCurrentThreadId( )));
}
}
VOID
SplOutSem(
VOID
)
{
if( hcsSpoolerSection ){
SPLASSERT( gpDbgPointers->pfnOutsideCritSec( hcsSpoolerSection ));
} else {
SPLASSERT( SpoolerSection.OwningThread != (HANDLE)((ULONG_PTR)GetCurrentThreadId( )));
}
}
#endif // DBG
VOID
EnterSplSem(
VOID
)
{
#if DBG
if( hcsSpoolerSection ){
gpDbgPointers->pfnEnterCritSec( hcsSpoolerSection );
} else {
EnterCriticalSection( &SpoolerSection );
}
#else
EnterCriticalSection( &SpoolerSection );
#endif
}
VOID
LeaveSplSem(
VOID
)
{
#if DBG
if( hcsSpoolerSection ){
gpDbgPointers->pfnLeaveCritSec( hcsSpoolerSection );
} else {
LeaveCriticalSection( &SpoolerSection );
}
#else
LeaveCriticalSection( &SpoolerSection );
#endif
}
PDEVMODE
AllocDevMode(
PDEVMODE pDevMode
)
{
PDEVMODE pDevModeAlloc = NULL;
DWORD Size;
if (pDevMode) {
Size = pDevMode->dmSize + pDevMode->dmDriverExtra;
if(pDevModeAlloc = AllocSplMem(Size)) {
memcpy(pDevModeAlloc, pDevMode, Size);
}
}
return pDevModeAlloc;
}
BOOL
FreeDevMode(
PDEVMODE pDevMode
)
{
if (pDevMode) {
FreeSplMem((PVOID)pDevMode);
return TRUE;
} else {
return FALSE;
}
}
PINIENTRY
FindName(
PINIENTRY pIniKey,
LPWSTR pName
)
{
if (pName) {
while (pIniKey) {
if (!lstrcmpi(pIniKey->pName, pName)) {
return pIniKey;
}
pIniKey=pIniKey->pNext;
}
}
return FALSE;
}
BOOL
FileExists(
LPWSTR pFileName
)
{
if( GetFileAttributes( pFileName ) == 0xffffffff ){
return FALSE;
}
return TRUE;
}
BOOL
DirectoryExists(
LPWSTR pDirectoryName
)
{
DWORD dwFileAttributes;
dwFileAttributes = GetFileAttributes( pDirectoryName );
if ( dwFileAttributes != 0xffffffff &&
dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
return TRUE;
}
return FALSE;
}
BOOL
CheckSepFile(
IN LPWSTR pFileName
)
{
BOOL bRetval = FALSE;
//
// NULL or "" is OK:
//
if (!pFileName || !*pFileName)
{
bRetval = TRUE;
}
else
{
//
// If the name is not NULL or "" then the name must be less
// than MAX_PATH and exist.
//
if ((wcslen(pFileName) < MAX_PATH-1) && FileExists(pFileName))
{
bRetval = TRUE;
}
}
return bRetval;
}
DWORD
GetFullNameFromId(
PINIPRINTER pIniPrinter,
DWORD JobId,
BOOL fJob,
LPWSTR pFileName,
BOOL Remote
)
{
DWORD i;
//
// MAX_PATH - 9 is tha maximum number of chars that we want to store in pFileName since we
// want to concatenate the SPL/SHD file
// If GetPrinterDirectory fails i is 0.
// The right way to fix this is that the caller of GetFullNameFromId chackes for the return value
// which is not the case.
//
i = GetPrinterDirectory(pIniPrinter, Remote, pFileName, MAX_PATH-9, pIniPrinter->pIniSpooler);
pFileName[i++]=L'\\';
wsprintf(&pFileName[i], L"%05d.%ws", JobId, fJob ? L"SPL" : L"SHD");
#ifdef PREVIOUS
for (i = 5; i--;) {
pFileName[i++] = (CHAR)((JobId % 10) + '0');
JobId /= 10;
}
#endif
while (pFileName[i++])
;
return i-1;
}
DWORD
GetPrinterDirectory(
PINIPRINTER pIniPrinter, // Can be NULL
BOOL Remote,
LPWSTR pDir,
DWORD MaxLength,
PINISPOOLER pIniSpooler
)
{
DWORD i=0;
LPWSTR psz;
if (Remote) {
DBGMSG(DBG_ERROR, ("GetPrinterDirectory called remotely. Not currently supported."));
return 0;
}
if ((pIniPrinter == NULL) || (pIniPrinter->pSpoolDir == NULL) ) {
if (pIniSpooler->pDefaultSpoolDir == NULL) {
//
// No default directory, then create a default. For cluster spoolers,
// the default directory is N:\Spool, where N is the shared drive letter
//
if( StrNCatBuff(pDir,
MaxLength,
pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ? pIniSpooler->pszClusResDriveLetter :
pIniSpooler->pDir,
L"\\",
pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ? szClusterPrinterDir : szPrinterDir,
NULL) != ERROR_SUCCESS ) {
return 0;
}
pIniSpooler->pDefaultSpoolDir = AllocSplStr(pDir);
} else {
// Give Caller the Default
wcscpy(pDir, pIniSpooler->pDefaultSpoolDir);
}
} else {
// Have Per Printer Directory
wcscpy (pDir, pIniPrinter->pSpoolDir);
}
return (wcslen(pDir));
}
DWORD
GetDriverDirectory(
LPWSTR pDir,
DWORD MaxLength,
PINIENVIRONMENT pIniEnvironment,
LPWSTR lpRemotePath,
PINISPOOLER pIniSpooler
)
{
LPWSTR psz;
if (lpRemotePath) {
if( StrNCatBuff(pDir,
MaxLength,
lpRemotePath,
L"\\",
pIniSpooler->pszDriversShare,
L"\\",
pIniEnvironment->pDirectory,
NULL) != ERROR_SUCCESS ) {
return 0;
}
} else {
if( StrNCatBuff( pDir,
MaxLength,
pIniSpooler->pDir,
L"\\",
szDriverDir,
L"\\",
pIniEnvironment->pDirectory,
NULL) != ERROR_SUCCESS ) {
return 0;
}
}
return wcslen(pDir);
}
DWORD
GetProcessorDirectory(
LPWSTR *pDir,
LPWSTR pEnvironment,
PINISPOOLER pIniSpooler
)
{
return StrCatAlloc(pDir,
pIniSpooler->pDir,
L"\\",
szPrintProcDir,
L"\\",
pEnvironment,
NULL);
}
PINIENTRY
FindIniKey(
PINIENTRY pIniEntry,
LPWSTR pName
)
{
if ( pName == NULL ) {
return NULL;
}
SplInSem();
while ( pIniEntry && lstrcmpi( pName, pIniEntry->pName ))
pIniEntry = pIniEntry->pNext;
return pIniEntry;
}
BOOL
CreateCompleteDirectory(
LPWSTR pDir
)
{
LPWSTR pBackSlash=pDir;
do {
pBackSlash = wcschr( pBackSlash, L'\\' );
if ( pBackSlash != NULL )
*pBackSlash = 0;
CreateDirectory(pDir, NULL);
if ( pBackSlash )
*pBackSlash++=L'\\';
} while ( pBackSlash );
// BUBUG Always returns TRUE
return TRUE;
}
LPCWSTR
FindFileName(
LPCWSTR pPathName
)
/*++
Routine Description:
Retrieve the filename portion of a path.
This will can the input string until it finds the last backslash,
then return the portion of the string immediately following it.
If the string terminates with a backslash, then NULL is returned.
Note: this can return illegal file names; no validation is done.
Arguments:
pPathName - Path name to parse.
Return Value:
Last portion of file name or NULL if none available.
--*/
{
LPCWSTR pSlash;
LPCWSTR pTemp;
if( !pPathName ){
return NULL;
}
pTemp = pPathName;
while( pSlash = wcschr( pTemp, L'\\' )) {
pTemp = pSlash+1;
}
if( !*pTemp ){
return NULL;
}
return pTemp;
}
LPWSTR
GetFileName(
LPWSTR pPathName
)
{
LPCWSTR pFileName;
pFileName = FindFileName( pPathName );
if (pFileName) {
return( AllocSplStr( pFileName ) );
} else {
return(NULL);
}
}
VOID
CreatePrintProcDirectory(
LPWSTR pEnvironment,
PINISPOOLER pIniSpooler
)
{
DWORD cb;
LPWSTR pEnd;
LPWSTR pPathName;
cb = wcslen(pIniSpooler->pDir)*sizeof(WCHAR) +
wcslen(pEnvironment)*sizeof(WCHAR) +
wcslen(szPrintProcDir)*sizeof(WCHAR) +
4*sizeof(WCHAR);
if (pPathName=AllocSplMem(cb)) {
wcscpy(pPathName, pIniSpooler->pDir);
pEnd=pPathName+wcslen(pPathName);
if( CreateDirectory(pPathName, NULL) ||
( GetLastError() == ERROR_ALREADY_EXISTS )) {
wcscpy(pEnd++, L"\\");
wcscpy(pEnd, szPrintProcDir);
if( CreateDirectory(pPathName, NULL) ||
( GetLastError() == ERROR_ALREADY_EXISTS )) {
pEnd+=wcslen(pEnd);
wcscpy(pEnd++, L"\\");
wcscpy(pEnd, pEnvironment);
if (CreateDirectory(pPathName, NULL) ||
(GetLastError() == ERROR_ALREADY_EXISTS)) {
pEnd+=wcslen(pEnd);
}
}
}
FreeSplMem(pPathName);
}
}
BOOL
RemoveFromList(
PINIENTRY *ppIniHead,
PINIENTRY pIniEntry
)
{
while (*ppIniHead && *ppIniHead != pIniEntry) {
ppIniHead = &(*ppIniHead)->pNext;
}
if (*ppIniHead)
*ppIniHead = (*ppIniHead)->pNext;
return(TRUE);
}
PKEYDATA
CreateTokenList(
LPWSTR pKeyData
)
{
DWORD cTokens;
DWORD cb;
PKEYDATA pResult;
LPWSTR pDest;
LPWSTR psz = pKeyData;
LPWSTR *ppToken;
if (!psz || !*psz)
return NULL;
cTokens=1;
// Scan through the string looking for commas,
// ensuring that each is followed by a non-NULL character:
while ((psz = wcschr(psz, L',')) && psz[1]) {
cTokens++;
psz++;
}
cb = sizeof(KEYDATA) + (cTokens-1) * sizeof(LPWSTR) +
wcslen(pKeyData)*sizeof(WCHAR) + sizeof(WCHAR);
if (!(pResult = (PKEYDATA)AllocSplMem(cb)))
return NULL;
// Initialise pDest to point beyond the token pointers:
pDest = (LPWSTR)((LPBYTE)pResult + sizeof(KEYDATA) +
(cTokens-1) * sizeof(LPWSTR));
// Then copy the key data buffer there:
wcscpy(pDest, pKeyData);
ppToken = pResult->pTokens;
psz = pDest;
do {
*ppToken++ = psz;
if ( psz = wcschr(psz, L',') )
*psz++ = L'\0';
} while (psz);
pResult->cTokens = cTokens;
return( pResult );
}
VOID
FreePortTokenList(
PKEYDATA pKeyData
)
{
PINIPORT pIniPort;
DWORD i;
if ( pKeyData ) {
if ( pKeyData->bFixPortRef ) {
for ( i = 0 ; i < pKeyData->cTokens ; ++i ) {
pIniPort = (PINIPORT)pKeyData->pTokens[i];
DECPORTREF(pIniPort);
}
}
FreeSplMem(pKeyData);
}
}
VOID
GetPrinterPorts(
PINIPRINTER pIniPrinter,
LPWSTR pszPorts,
DWORD *pcbNeeded
)
{
PINIPORT pIniPort;
BOOL Comma;
DWORD i;
DWORD cbNeeded = 0;
SPLASSERT(pcbNeeded);
// Determine required size
Comma = FALSE;
for ( i = 0 ; i < pIniPrinter->cPorts ; ++i ) {
pIniPort = pIniPrinter->ppIniPorts[i];
if ( pIniPort->Status & PP_FILE )
continue;
if ( Comma )
cbNeeded += wcslen(szComma)*sizeof(WCHAR);
cbNeeded += wcslen(pIniPort->pName)*sizeof(WCHAR);
Comma = TRUE;
}
//
// Add in size of NULL
//
cbNeeded += sizeof(WCHAR);
if (pszPorts && cbNeeded <= *pcbNeeded) {
//
// If we are given a buffer & buffer is big enough, then fill it
//
Comma = FALSE;
for ( i = 0 ; i < pIniPrinter->cPorts ; ++i ) {
pIniPort = pIniPrinter->ppIniPorts[i];
if ( pIniPort->Status & PP_FILE )
continue;
if ( Comma ) {
wcscat(pszPorts, szComma);
wcscat(pszPorts, pIniPort->pName);
} else {
wcscpy(pszPorts, pIniPort->pName);
}
Comma = TRUE;
}
}
*pcbNeeded = cbNeeded;
}
BOOL
MyName(
LPWSTR pName,
PINISPOOLER pIniSpooler
)
{
EnterSplSem();
if (CheckMyName(pName, pIniSpooler))
{
LeaveSplSem();
return TRUE;
}
//
// Only refresh machine names if pName is an IP or DNS address
//
if (pIniSpooler == pLocalIniSpooler &&
wcschr(pName, L'.') &&
RefreshMachineNamesCache() &&
CheckMyName(pName, pIniSpooler))
{
LeaveSplSem();
return TRUE;
}
SetLastError(ERROR_INVALID_NAME);
LeaveSplSem();
return FALSE;
}
BOOL
RefreshMachineNamesCache(
)
{
PWSTR *ppszOtherNames;
DWORD cOtherNames;
SplInSem();
//
// Get other machine names first. Only if it succeeds do we replace the cache.
//
if (!BuildOtherNamesFromMachineName(&ppszOtherNames, &cOtherNames))
return FALSE;
FreeOtherNames(&pLocalIniSpooler->ppszOtherNames, &pLocalIniSpooler->cOtherNames);
pLocalIniSpooler->ppszOtherNames = ppszOtherNames;
pLocalIniSpooler->cOtherNames = cOtherNames;
return TRUE;
}
BOOL
CheckMyName(
LPWSTR pName,
PINISPOOLER pIniSpooler
)
{
DWORD dwIndex = 0;
if (!pName || !*pName)
return TRUE;
if (*pName == L'\\' && *(pName+1) == L'\\') {
if (!lstrcmpi(pName, pIniSpooler->pMachineName))
return TRUE;
while (dwIndex < pIniSpooler->cOtherNames) {
if (!lstrcmpi(pName+2, pIniSpooler->ppszOtherNames[dwIndex]))
return TRUE;
++dwIndex;
}
}
return FALSE;
}
BOOL
GetSid(
PHANDLE phToken
)
{
if (!OpenThreadToken(GetCurrentThread(),
TOKEN_IMPERSONATE | TOKEN_QUERY,
TRUE,
phToken)) {
DBGMSG(DBG_WARNING, ("OpenThreadToken failed: %d\n", GetLastError()));
return FALSE;
} else
return TRUE;
}
BOOL
SetCurrentSid(
HANDLE hToken
)
{
#if DBG
WCHAR UserName[256];
DWORD cbUserName=256;
if( MODULE_DEBUG & DBG_TRACE )
GetUserName(UserName, &cbUserName);
DBGMSG(DBG_TRACE, ("SetCurrentSid BEFORE: user name is %ws\n", UserName));
#endif
//
// Normally the function SetCurrentSid is not supposed to change the last error
// of the routine where it is called. NtSetInformationThread conveniently returns
// a status and does not touch the last error.
//
NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken,
&hToken, sizeof(hToken));
#if DBG
cbUserName = 256;
if( MODULE_DEBUG & DBG_TRACE )
GetUserName(UserName, &cbUserName);
DBGMSG(DBG_TRACE, ("SetCurrentSid AFTER: user name is %ws\n", UserName));
#endif
return TRUE;
}
LPWSTR
GetErrorString(
DWORD Error
)
{
WCHAR Buffer1[512];
LPWSTR pErrorString=NULL;
DWORD dwFlags;
HANDLE hModule = NULL;
if ((Error >= NERR_BASE) && (Error <= MAX_NERR)) {
dwFlags = FORMAT_MESSAGE_FROM_HMODULE;
hModule = LoadLibrary(szNetMsgDll);
} else {
dwFlags = FORMAT_MESSAGE_FROM_SYSTEM;
hModule = NULL;
}
//
// Only display out of paper and device disconnected errors.
//
if ((Error == ERROR_NOT_READY ||
Error == ERROR_OUT_OF_PAPER ||
Error == ERROR_DEVICE_REINITIALIZATION_NEEDED ||
Error == ERROR_DEVICE_REQUIRES_CLEANING ||
Error == ERROR_DEVICE_DOOR_OPEN ||
Error == ERROR_DEVICE_NOT_CONNECTED) &&
FormatMessage(dwFlags,
hModule,
Error,
0,
Buffer1,
COUNTOF(Buffer1),
NULL)) {
EnterSplSem();
pErrorString = AllocSplStr(Buffer1);
LeaveSplSem();
}
if (hModule) {
FreeLibrary(hModule);
}
return pErrorString;
}
#define NULL_TERMINATED 0
INT
AnsiToUnicodeString(
LPSTR pAnsi,
LPWSTR pUnicode,
DWORD StringLength
)
/*++
Routine Description:
Converts an Ansi String to a UnicodeString
Arguments:
pAnsi - A valid source ANSI string.
pUnicode - A pointer to a buffer large enough to accommodate
the converted string.
StringLength - The length of the source ANSI string.
If 0 (NULL_TERMINATED), the string is assumed to be
null-terminated.
Return Value:
The return value from MultiByteToWideChar, the number of
wide characters returned.
andrewbe, 11 Jan 1993
--*/
{
if( StringLength == NULL_TERMINATED )
StringLength = strlen( pAnsi );
return MultiByteToWideChar( CP_ACP,
MB_PRECOMPOSED,
pAnsi,
StringLength + 1,
pUnicode,
StringLength + 1 );
}
INT
Message(
HWND hwnd,
DWORD Type,
int CaptionID,
int TextID, ...)
{
/*++
Routine Description:
Displays a message by loading the strings whose IDs are passed into
the function, and substituting the supplied variable argument list
using the varargs macros.
Arguments:
hwnd Window Handle
Type
CaptionID
TextId
Return Value:
--*/
WCHAR MsgText[512];
WCHAR MsgFormat[256];
WCHAR MsgCaption[40];
va_list vargs;
if( ( LoadString( hInst, TextID, MsgFormat,
sizeof MsgFormat / sizeof *MsgFormat ) > 0 )
&& ( LoadString( hInst, CaptionID, MsgCaption,
sizeof MsgCaption / sizeof *MsgCaption ) > 0 ) )
{
va_start( vargs, TextID );
_vsntprintf( MsgText, COUNTOF(MsgText)-1, MsgFormat, vargs );
MsgText[COUNTOF(MsgText)-1] = 0;
va_end( vargs );
return MessageBox(hwnd, MsgText, MsgCaption, Type);
}
else
return 0;
}
typedef struct {
DWORD Message;
WPARAM wParam;
LPARAM lParam;
} MESSAGE, *PMESSAGE;
// The Broadcasts are done on a separate thread, the reason it CSRSS
// will create a server side thread when we call user and we don't want
// that to be paired up with the RPC thread which is in the spooss server.
// We want it to go away the moment we have completed the SendMessage.
// We also call SendNotifyMessage since we don't care if the broadcasts
// are syncronous this uses less resources since usually we don't have more
// than one broadcast.
//
// TESTING
//
DWORD dwSendFormMessage = 0;
VOID
SplBroadcastChange(
HANDLE hPrinter,
DWORD Message,
WPARAM wParam,
LPARAM lParam
)
{
PSPOOL pSpool = (PSPOOL)hPrinter;
PINISPOOLER pIniSpooler;
if (ValidateSpoolHandle( pSpool, 0 )) {
pIniSpooler = pSpool->pIniSpooler;
BroadcastChange(pIniSpooler, Message, wParam, lParam);
}
}
VOID
BroadcastChange(
IN PINISPOOLER pIniSpooler,
IN DWORD Message,
IN WPARAM wParam,
IN LPARAM lParam
)
{
if (( pIniSpooler != NULL ) && ( pIniSpooler->SpoolerFlags & SPL_BROADCAST_CHANGE )) {
BOOL bIsTerminalServerInstalled = (USER_SHARED_DATA->SuiteMask & (1 << TerminalServer));
//
// Currently we cannot determine if the TermService process is running, so at the momemt
// we assume it is always running.
//
BOOL bIsTerminalServerRunning = TRUE;
//
// If terminal server is installed and enabled then load the winsta.dll if not already
// loaded and get the send window message function.
//
if ( bIsTerminalServerInstalled && !pfWinStationSendWindowMessage ) {
//
// The winstadllhandle is shared among other files in the spooler, so don't
// load the dll again if it is already loaded. Note: we are not in a critical
// section because winsta.dll is never unload, hence if there are two threads
// that execute this code at the same time we may potenially load the library
// twice.
//
if ( !WinStaDllHandle ) {
UINT uOldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
WinStaDllHandle = LoadLibraryW(L"winsta.dll");
SetErrorMode(uOldErrorMode);
}
if ( WinStaDllHandle ) {
pfWinStationSendWindowMessage = (pfnWinStationSendWindowMessage)GetProcAddress( WinStaDllHandle,
"WinStationSendWindowMessage" );
}
}
if ( pfWinStationSendWindowMessage ) {
//
// Only send the message to the session that orginated
// the call, this will go to the console session when a
// change is made by a remote client.
//
LONG Response = 0;
LONG lRetval = FALSE;
HANDLE hToken = NULL;
ULONG uSession = GetClientSessionId();
//
// It appears the WinStationSendWindowMessage function has to
// be in system context if the impersonating user is not an
// an admin on the machine.
//
hToken = RevertToPrinterSelf();
lRetval = pfWinStationSendWindowMessage( SERVERNAME_CURRENT,
uSession,
1, // Wait at most one second
HandleToULong(HWND_BROADCAST),
Message,
wParam,
lParam,
&Response );
ImpersonatePrinterClient(hToken);
}
//
// We send the message normally if we have a null pfWinstationSendWindowMessage
// function or if terminal server is not running.
//
if ( !pfWinStationSendWindowMessage || !bIsTerminalServerRunning ){
SendNotifyMessage( HWND_BROADCAST,
Message,
wParam,
lParam );
}
} else {
DBGMSG(DBG_TRACE, ("BroadCastChange Ignoring Change\n"));
}
}
VOID
MyMessageBeep(
DWORD fuType,
PINISPOOLER pIniSpooler
)
{
if ( pIniSpooler->dwBeepEnabled != 0 ) {
MessageBeep(fuType);
}
}
// Recursively delete any subkeys of a given key.
// Assumes that RevertToPrinterSelf() has been called.
DWORD
DeleteSubkeys(
HKEY hKey,
PINISPOOLER pIniSpooler
)
{
DWORD cchData;
WCHAR SubkeyName[MAX_PATH];
HKEY hSubkey;
LONG Status;
cchData = COUNTOF( SubkeyName );
while ((Status = SplRegEnumKey( hKey,
0,
SubkeyName,
&cchData,
NULL,
pIniSpooler )) == ERROR_SUCCESS ) {
Status = SplRegCreateKey( hKey,
SubkeyName,
0,
KEY_READ | KEY_WRITE,
NULL,
&hSubkey,
NULL,
pIniSpooler );
if( Status == ERROR_SUCCESS ) {
Status = DeleteSubkeys( hSubkey, pIniSpooler );
SplRegCloseKey( hSubkey, pIniSpooler);
if( Status == ERROR_SUCCESS )
SplRegDeleteKey( hKey, SubkeyName, pIniSpooler );
}
//
// N.B. Don't increment since we've deleted the zeroth item.
//
cchData = COUNTOF( SubkeyName );
}
if( Status == ERROR_NO_MORE_ITEMS )
Status = ERROR_SUCCESS;
return Status;
}
long Myatol(LPWSTR nptr)
{
int c; // current char
long total; // current total
int sign; // if '-', then negative, otherwise positive
// skip whitespace
while (isspace(*nptr))
++nptr;
c = *nptr++;
sign = c; // save sign indication
if (c == '-' || c == '+')
c = *nptr++; // skip sign
total = 0;
while (isdigit(c)) {
total = 10 * total + (c - '0'); // accumulate digit
c = *nptr++; // get next char
}
if (sign == '-')
return -total;
else
return total; // return result, negated if necessary
}
ULONG_PTR
atox(
LPCWSTR psz
)
/*++
Routine Description:
Converts a string to a hex value, skipping any leading
white space. Cannot be uppercase, cannot contain leading 0x.
Arguments:
psz - pointer to hex string that needs to be converted. This string
can have leading characters, but MUST be lowercase.
Return Value:
DWORD value.
--*/
{
ULONG_PTR Value = 0;
ULONG_PTR Add;
_wcslwr((LPWSTR)psz);
while( isspace( *psz )){
++psz;
}
for( ;; ++psz ){
if( *psz >= TEXT( '0' ) && *psz <= TEXT( '9' )){
Add = *psz - TEXT( '0' );
} else if( *psz >= TEXT( 'a' ) && *psz <= TEXT( 'f' )){
Add = *psz - TEXT( 'a' ) + 0xa;
} else {
break;
}
Value *= 0x10;
Value += Add;
}
return Value;
}
BOOL
ValidateSpoolHandle(
PSPOOL pSpool,
DWORD dwDisallowMask
)
{
BOOL ReturnValue;
try {
//
// Zombied handles should return back error. The client
// side will see ERROR_INVALID_HANDLE, close it and revalidate.
//
if (( pSpool == NULL ) ||
( pSpool == INVALID_HANDLE_VALUE ) ||
( pSpool->Status & SPOOL_STATUS_ZOMBIE ) ||
( pSpool->signature != SJ_SIGNATURE ) ||
( pSpool->TypeofHandle & dwDisallowMask ) ||
( pSpool->TypeofHandle & PRINTER_HANDLE_XCV_PORT ) ||
( pSpool->pIniSpooler->signature != ISP_SIGNATURE ) ||
( ( pSpool->TypeofHandle & PRINTER_HANDLE_PRINTER ) &&
( pSpool->pIniPrinter->signature !=IP_SIGNATURE ) )) {
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
}
}except (1) {
ReturnValue = FALSE;
}
if ( !ReturnValue )
SetLastError( ERROR_INVALID_HANDLE );
return ReturnValue;
}
BOOL
UpdateString(
LPWSTR* ppszCur,
LPWSTR pszNew)
{
//
// !! LATER !!
//
// Replace with non-nls wcscmp since we want byte comparison and
// only care if the strings are different (ignore ordering).
//
if ((!*ppszCur || !**ppszCur) && (!pszNew || !*pszNew))
return FALSE;
if (!*ppszCur || !pszNew || wcscmp(*ppszCur, pszNew)) {
ReallocSplStr(ppszCur, pszNew);
return TRUE;
}
return FALSE;
}
BOOL
CreateDirectoryWithoutImpersonatingUser(
LPWSTR pDirectory
)
/*++
Routine Description:
This routine stops impersonating the user and creates a directory
Arguments:
pDirectory - Fully Qualified path of directory.
Return Value:
TRUE - Success
FALSE - failed ( call GetLastError )
--*/
{
HANDLE hToken = INVALID_HANDLE_VALUE;
BOOL bReturnValue;
SPLASSERT( pDirectory != NULL );
hToken = RevertToPrinterSelf();
bReturnValue = CreateDirectory( pDirectory, NULL );
if ( bReturnValue == FALSE ) {
DBGMSG( DBG_WARNING, ("CreateDirectoryWithoutImpersonatingUser failed CreateDirectory %ws error %d\n", pDirectory, GetLastError() ));
}
if ( hToken != INVALID_HANDLE_VALUE ) {
ImpersonatePrinterClient(hToken);
}
return bReturnValue;
}
BOOL
DeleteAllFilesInDirectory(
LPWSTR pDirectory,
BOOL bWaitForReboot
)
/*++
Routine Description:
Deletes all files the specified directory
If it can't be deleted it gets marked for deletion on next reboot.
Arguments:
pDirectory - Fully Qualified path of directory.
bWaitForReboot - Don't delete the files until a reboot
Return Value:
TRUE - Success
FALSE - failed something major, like allocating memory.
--*/
{
BOOL bReturnValue = FALSE;
HANDLE hFindFile;
WIN32_FIND_DATA FindData;
WCHAR ScratchBuffer[ MAX_PATH ];
DBGMSG( DBG_TRACE, ("DeleteAllFilesInDirectory: bWaitForReboot = %\n", bWaitForReboot ));
SPLASSERT( pDirectory != NULL );
if (StrNCatBuff(ScratchBuffer, COUNTOF(ScratchBuffer), pDirectory, L"\\*", NULL) != ERROR_SUCCESS)
return FALSE;
hFindFile = FindFirstFile( ScratchBuffer, &FindData );
if ( hFindFile != INVALID_HANDLE_VALUE ) {
do {
//
// Don't Attempt to Delete Directories
//
if ( !( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) {
//
// Fully Qualified Path
//
if (StrNCatBuff( ScratchBuffer,
COUNTOF(ScratchBuffer),
pDirectory,
L"\\",
FindData.cFileName,
NULL) == ERROR_SUCCESS) {
if ( bWaitForReboot || !DeleteFile( ScratchBuffer ) ) {
DBGMSG( DBG_WARNING, ("DeleteAllFilesInDirectory failed DeleteFile( %ws ) error %d\n", ScratchBuffer, GetLastError() ));
if ( !MoveFileEx( ScratchBuffer, NULL, MOVEFILE_DELAY_UNTIL_REBOOT ) ) {
DBGMSG( DBG_WARNING, ("DeleteAllFilesInDirectory failed MoveFileEx %ws error %d\n", ScratchBuffer, GetLastError() ));
} else {
DBGMSG( DBG_TRACE, ("MoveFileEx %ws Delay until reboot OK\n", ScratchBuffer ));
}
} else {
DBGMSG( DBG_TRACE, ("Deleted %ws OK\n", ScratchBuffer ));
}
}
}
} while( FindNextFile( hFindFile, &FindData ) );
bReturnValue = FindClose( hFindFile );
} else {
DBGMSG( DBG_WARNING, ("DeleteOldDrivers failed findfirst ( %ws ), error %d\n", ScratchBuffer, GetLastError() ));
}
return bReturnValue;
}
BOOL
DeleteAllFilesAndDirectory(
LPWSTR pDirectory,
BOOL bWaitForReboot
)
/*++
Routine Description:
Deletes all files the specified directory, then deletes the directory.
If the Directory cannot be deleted right away, it is set to be deleted
at reboot time.
Security NOTE - This routine runs as SYSTEM, not imperonating the user
Arguments:
pDirectory - Fully Qualified path of directory.
Return Value:
TRUE - Success
FALSE - failed something major, like allocating memory.
--*/
{
BOOL bReturnValue;
HANDLE hToken = INVALID_HANDLE_VALUE;
DBGMSG( DBG_TRACE, ("DeleteAllFilesAndDirectory: bWaitForReboot = %d\n", bWaitForReboot ));
hToken = RevertToPrinterSelf();
if( bReturnValue = DeleteAllFilesInDirectory( pDirectory, bWaitForReboot ) ) {
if ( bWaitForReboot || !RemoveDirectory( pDirectory )) {
if (!SplMoveFileEx( pDirectory, NULL, MOVEFILE_DELAY_UNTIL_REBOOT )) {
DBGMSG( DBG_WARNING, ("DeleteAllFilesAndDirectory failed to delete %ws until reboot %d\n", pDirectory, GetLastError() ));
} else {
DBGMSG( DBG_TRACE, ( "DeleteAllFilesAndDirectory: MoveFileEx Delay until reboot OK\n" ));
}
} else {
DBGMSG( DBG_TRACE, ("DeleteAllFilesAndDirectory deleted %ws OK\n", pDirectory ));
}
}
if ( hToken != INVALID_HANDLE_VALUE ) {
ImpersonatePrinterClient(hToken);
}
return bReturnValue;
}
VOID
DeleteDirectoryRecursively(
LPCWSTR pszDirectory,
BOOL bWaitForReboot
)
/*++
Routine Name:
DeleteDirectoryRecursively
Routine Description:
Recursively Deletes the specified directory
If it can't be deleted it gets marked for deletion on next reboot.
Arguments:
pDirectory - Fully Qualified path of directory.
bWaitForReboot - Don't delete the files until a reboot
Return Value:
Nothing.
--*/
{
HANDLE hFindFile;
WIN32_FIND_DATA FindData;
WCHAR ScratchBuffer[ MAX_PATH ];
if ( pszDirectory &&
StrNCatBuff(ScratchBuffer,
COUNTOF(ScratchBuffer),
pszDirectory,
L"\\*",
NULL) == ERROR_SUCCESS ) {
hFindFile = FindFirstFile(ScratchBuffer, &FindData);
if ( hFindFile != INVALID_HANDLE_VALUE ) {
do {
//
// Don't delete current and parent directory.
//
if (wcscmp(FindData.cFileName, L".") != 0 &&
wcscmp(FindData.cFileName, L"..") != 0 &&
StrNCatBuff( ScratchBuffer,
COUNTOF(ScratchBuffer),
pszDirectory,
L"\\",
FindData.cFileName,
NULL) == ERROR_SUCCESS) {
if (!(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
if (bWaitForReboot || !DeleteFile(ScratchBuffer)) {
//
// Delete the file on reboot if asked or if deletion failed.
//
SplMoveFileEx(ScratchBuffer, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
}
} else {
//
// Delete subdirectory
//
DeleteAllFilesAndDirectory(ScratchBuffer, bWaitForReboot);
}
}
} while (FindNextFile(hFindFile, &FindData));
FindClose(hFindFile);
if (bWaitForReboot || !RemoveDirectory(pszDirectory)) {
//
// Delete the directory on reboot if asked or if deletion failed.
//
SplMoveFileEx(pszDirectory, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
}
}
}
return;
}
DWORD
CreateNumberedTempDirectory(
IN LPCWSTR pszDirectory,
OUT LPWSTR *ppszTempDirectory
)
/*++
Routine Name:
CreateNumberedTempDirectory
Routine Description:
Creates a temporary subdirectory named 1... 500
The lenght of ppTempDirectory cannot be bigger than MAX_PATH
Returns the number of directory created or -1 for failure
Arguments:
pszDirectory - directory where to created temporary
ppszTempDirectory - path of the new temporary directory
Return Value:
If success, returns the number of directory created.
Returns -1 if a failure occurs.
--*/
{
DWORD dwIndex, dwTempDir;
WCHAR szTempDir[4];
WCHAR *pszTemporary = NULL;
dwTempDir = -1;
if (pszDirectory && ppszTempDirectory)
{
*ppszTempDirectory = NULL;
if (pszTemporary = AllocSplMem((wcslen(pszDirectory) + COUNTOF(szTempDir) + 1) * sizeof(WCHAR)))
{
for (dwIndex = 1; dwIndex < 500; ++dwIndex)
{
_itow(dwIndex, szTempDir, 10);
if (StrNCatBuff(pszTemporary,
MAX_PATH,
pszDirectory,
L"\\",
szTempDir,
NULL) == ERROR_SUCCESS &&
!DirectoryExists(pszTemporary) &&
CreateDirectory(pszTemporary, NULL))
{
dwTempDir = dwIndex;
break;
}
}
}
}
if (dwTempDir != -1)
{
*ppszTempDirectory = pszTemporary;
}
else
{
SetLastError(ERROR_NO_SYSTEM_RESOURCES);
FreeSplMem(pszTemporary);
}
return dwTempDir;
}
//
// Dependent file fields are LPWSTR field of filenames separated by \0
// and terminated by \0\0
// 2 such fields are same if same set of filenames appear
// (order of filenames does not matter)
//
BOOL
SameMultiSz(
LPWSTR pszz1,
LPWSTR pszz2
)
{
LPWSTR psz1, psz2;
if ( !pszz1 && !pszz2 )
return TRUE;
if ( !pszz1 || !pszz2 )
return FALSE;
//
// Check there are same number of strings
//
for ( psz1 = pszz1, psz2 = pszz2 ;
*psz1 && *psz2 ;
psz1 += wcslen(psz1)+1, psz2 += wcslen(psz2)+1 )
;
//
// If different number of strings return FALSE
//
if ( *psz1 || *psz2 )
return FALSE;
//
// Check in pszz2 for each string in pszz1
//
for ( psz1 = pszz1 ; *psz1 ; psz1 += wcslen(psz1) + 1 ) {
for ( psz2 = pszz2 ;
*psz2 && _wcsicmp(psz1, psz2) ;
psz2 += wcslen(psz2) + 1 )
;
//
// Did we find psz1 in pszz2
//
if ( ! *psz2 ) {
return FALSE;
}
}
return TRUE;
}
int
wstrcmpEx(
LPCWSTR s1,
LPCWSTR s2,
BOOL bCaseSensitive
)
{
if ( s1 && *s1 ) {
if ( s2 && *s2 ) {
return bCaseSensitive ? wcscmp(s1, s2) : _wcsicmp(s1, s2);
}
else {
return 1;
}
}
else {
if ( s2 && *s2 ) {
return -1;
}
else {
return 0;
}
}
}
BOOL
RegSetString(
HANDLE hKey,
LPWSTR pValueName,
LPWSTR pStringValue,
PDWORD pdwLastError,
PINISPOOLER pIniSpooler
)
{
BOOL bReturnValue;
LPWSTR pString;
DWORD cbString;
DWORD Status;
if ( pStringValue ) {
pString = pStringValue;
cbString = ( wcslen( pStringValue ) + 1 )*sizeof(WCHAR);
} else {
pString = szNull;
cbString = sizeof(WCHAR);
}
Status = SplRegSetValue( hKey,
pValueName,
REG_SZ,
(LPBYTE)pString,
cbString,
pIniSpooler );
if ( Status != ERROR_SUCCESS ) {
DBGMSG( DBG_WARNING, ("RegSetString value %ws string %ws error %d\n", pValueName, pString, Status ));
*pdwLastError = Status;
bReturnValue = FALSE;
} else {
bReturnValue = TRUE;
}
return bReturnValue;
}
BOOL
RegSetDWord(
HANDLE hKey,
LPWSTR pValueName,
DWORD dwParam,
PDWORD pdwLastError,
PINISPOOLER pIniSpooler
)
{
BOOL bReturnValue;
LPWSTR pString;
DWORD Status;
Status = SplRegSetValue( hKey,
pValueName,
REG_DWORD,
(LPBYTE)&dwParam,
sizeof(DWORD),
pIniSpooler );
if ( Status != ERROR_SUCCESS ) {
DBGMSG( DBG_WARNING, ("RegSetDWord value %ws DWORD %x error %d\n",
pValueName, dwParam, Status ));
*pdwLastError = Status;
bReturnValue = FALSE;
} else {
bReturnValue = TRUE;
}
return bReturnValue;
}
BOOL
RegSetBinaryData(
HKEY hKey,
LPWSTR pValueName,
LPBYTE pData,
DWORD cbData,
PDWORD pdwLastError,
PINISPOOLER pIniSpooler
)
{
DWORD Status;
BOOL bReturnValue;
Status = SplRegSetValue( hKey,
pValueName,
REG_BINARY,
pData,
cbData,
pIniSpooler );
if ( Status != ERROR_SUCCESS ) {
DBGMSG( DBG_WARNING, ("RegSetBinaryData Value %ws pData %x cbData %d error %d\n",
pValueName,
pData,
cbData,
Status ));
bReturnValue = FALSE;
*pdwLastError = Status;
} else {
bReturnValue = TRUE;
}
return bReturnValue;
}
BOOL
RegSetMultiString(
HANDLE hKey,
LPWSTR pValueName,
LPWSTR pStringValue,
DWORD cchString,
PDWORD pdwLastError,
PINISPOOLER pIniSpooler
)
{
BOOL bReturnValue;
DWORD Status;
LPWSTR pString;
WCHAR szzNull[2];
if ( pStringValue ) {
pString = pStringValue;
cchString *= sizeof(WCHAR);
} else {
szzNull[0] = szzNull[1] = '\0';
pString = szNull;
cchString = 2 * sizeof(WCHAR);
}
Status = SplRegSetValue( hKey,
pValueName,
REG_MULTI_SZ,
(LPBYTE)pString,
cchString,
pIniSpooler );
if ( Status != ERROR_SUCCESS ) {
DBGMSG( DBG_WARNING, ("RegSetMultiString value %ws string %ws error %d\n", pValueName, pString, Status ));
*pdwLastError = Status;
bReturnValue = FALSE;
} else {
bReturnValue = TRUE;
}
return bReturnValue;
}
BOOL
RegGetString(
HANDLE hKey,
LPWSTR pValueName,
LPWSTR *ppValue,
LPDWORD pcchValue,
PDWORD pdwLastError,
BOOL bFailIfNotFound,
PINISPOOLER pIniSpooler
)
/*++
Routine Description:
Allocates memory and reads a value from Registry for a value which was
earlier set by calling RegSetValueEx.
Arguments:
hKey : currently open key to be used to query the registry
pValueName : value to be used to query the registry
ppValue : on return value of TRUE *ppValue (memory allocated by
the routine) will have the value
pdwLastError : on failure *dwLastError will give the error
bFailIfNotFound : Tells if the field is mandatory (if not found error)
Return Value:
TRUE : value is found and succesfully read.
Memory will be allocated to hold the value
FALSE: Value was not read.
If bFailIfNotFound was TRUE error code will be set.
History:
Written by MuhuntS (Muhunthan Sivapragasam)June 95
--*/
{
BOOL bReturnValue = TRUE;
LPWSTR pString;
DWORD cbValue;
DWORD Status, Type;
//
// First query to find out size
//
cbValue = 0;
Status = SplRegQueryValue( hKey,
pValueName,
&Type,
NULL,
&cbValue,
pIniSpooler );
if ( Status != ERROR_SUCCESS ) {
// Set error code only if it is a required field
if ( bFailIfNotFound )
*pdwLastError = Status;
bReturnValue = FALSE;
} else if ( (Type == REG_SZ && cbValue > sizeof(WCHAR) ) ||
(Type == REG_MULTI_SZ && cbValue > 2*sizeof(WCHAR)) ) {
//
// Something (besides \0 or \0\0) to read
//
if ( !(*ppValue=AllocSplMem(cbValue) ) ) {
*pdwLastError = GetLastError();
bReturnValue = FALSE;
} else {
Status = SplRegQueryValue( hKey,
pValueName,
&Type,
(LPBYTE)*ppValue,
&cbValue,
pIniSpooler );
if ( Status != ERROR_SUCCESS ) {
DBGMSG( DBG_WARNING, ("RegGetString value %ws string %ws error %d\n", pValueName, **ppValue, Status ));
*pdwLastError = Status;
bReturnValue = FALSE;
} else {
*pcchValue = cbValue / sizeof(WCHAR);
bReturnValue = TRUE;
}
}
}
return bReturnValue;
}
BOOL
RegGetMultiSzString(
HANDLE hKey,
LPWSTR pValueName,
LPWSTR *ppValue,
LPDWORD pcchValue,
PDWORD pdwLastError,
BOOL bFailIfNotFound,
PINISPOOLER pIniSpooler
)
/*++
Routine Description:
Duplicate function for RegGetString. Handles multi-sz strings so that Spooler
doesn't crash.
Arguments:
hKey : currently open key to be used to query the registry
pValueName : value to be used to query the registry
ppValue : on return value of TRUE *ppValue (memory allocated by
the routine) will have the value
pdwLastError : on failure *dwLastError will give the error
bFailIfNotFound : Tells if the field is mandatory (if not found error)
Return Value:
TRUE : value is found and succesfully read.
Memory will be allocated to hold the value
FALSE: Value was not read.
If bFailIfNotFound was TRUE error code will be set.
History:
Written by AdinaTru. This function is a fix for the case when 3rd party applications
install drivers by writing registry string values instead of multi-sz. This causes Spooler
to AV because it will handle a string as a multi-sz string. The goal of having this function
was to provide a quick fix/low regression risk for XP RC2 release. A bug was opened for rewriting
RegGetMultiSzString and RegGetString in BlackComb timeframe.
--*/
{
BOOL bReturnValue = TRUE;
LPWSTR pString;
DWORD cbValue;
DWORD Status, Type;
//
// First query to find out size
//
cbValue = 0;
Status = SplRegQueryValue( hKey,
pValueName,
&Type,
NULL,
&cbValue,
pIniSpooler );
if ( Status != ERROR_SUCCESS ) {
// Set error code only if it is a required field
if ( bFailIfNotFound )
*pdwLastError = Status;
bReturnValue = FALSE;
} else if ( (Type == REG_SZ && cbValue > sizeof(WCHAR) ) ||
(Type == REG_MULTI_SZ && cbValue > 2*sizeof(WCHAR)) ) {
//
// Something (besides \0 or \0\0) to read
//
//
// We expect a REG_MULTI_SZ string. Add an extra zero so Spooler doesn't crash.
// XP RC2 fix.
//
if (Type == REG_SZ) {
cbValue += sizeof(WCHAR);
}
if ( !(*ppValue=AllocSplMem(cbValue) ) ) {
*pdwLastError = GetLastError();
bReturnValue = FALSE;
} else {
Status = SplRegQueryValue( hKey,
pValueName,
&Type,
(LPBYTE)*ppValue,
&cbValue,
pIniSpooler );
if ( Status != ERROR_SUCCESS ) {
DBGMSG( DBG_WARNING, ("RegGetString value %ws string %ws error %d\n", pValueName, **ppValue, Status ));
*pdwLastError = Status;
bReturnValue = FALSE;
//
// Caller will must the memory regardless of success or failure.
//
} else {
*pcchValue = cbValue / sizeof(WCHAR);
bReturnValue = TRUE;
}
}
}
return bReturnValue;
}
VOID
FreeStructurePointers(
LPBYTE lpStruct,
LPBYTE lpStruct2,
LPDWORD lpOffsets)
/*++
Routine Description:
This routine frees memory allocated to all the pointers in the structure
If lpStruct2 is specified only pointers in lpStruct which are different
than the ones in lpStruct will be freed
Arguments:
lpStruct: Pointer to the structure
lpStruct2: Pointer to the structure to compare with (optional)
lpOffsets: An array of DWORDS (terminated by -1) givings offsets in the
structure which have memory which needs to be freed
Return Value:
nothing
History:
MuhuntS -- Aug 95
--*/
{
register INT i;
if ( lpStruct2 ) {
for( i=0; lpOffsets[i] != 0xFFFFFFFF; ++i ) {
if ( *(LPBYTE *) (lpStruct+lpOffsets[i]) &&
*(LPBYTE *) (lpStruct+lpOffsets[i]) !=
*(LPBYTE *) (lpStruct2+lpOffsets[i]) )
FreeSplMem(*(LPBYTE *) (lpStruct+lpOffsets[i]));
}
} else {
for( i=0; lpOffsets[i] != 0xFFFFFFFF; ++i ) {
if ( *(LPBYTE *) (lpStruct+lpOffsets[i]) )
FreeSplMem(*(LPBYTE *) (lpStruct+lpOffsets[i]));
}
}
}
/*++
Routine Name:
AllocOrUpdateStringAndTestSame
Routine Description:
This routine can be used to do an atomic update of values in a structure.
Create a temporary structure and copy the old structure to it.
Then call this routine for all LPWSTR fields to check and update strings
If the value changes:
This routine will allocate memory and assign pointer in the
temporary structure.
Arguments:
ppString : Points to a pointer in the temporary sturucture
pNewValue : New value to be set
pOldValue : Value in the original strucutre
bCaseSensitive : Determines whether case-sensitive string compare is performed
pbFail : On error set this to TRUE (Note: it could already be TRUE)
*pbIdentical : If the strings are diferent this is set to FALSE.
(Could already be false).
Return Value:
TRUE on success, else FALSE
--*/
BOOL
AllocOrUpdateStringAndTestSame(
IN LPWSTR *ppString,
IN LPCWSTR pNewValue,
IN LPCWSTR pOldValue,
IN BOOL bCaseSensitive,
IN OUT BOOL *pbFail,
IN OUT BOOL *pbIdentical
)
{
BOOL bReturn = TRUE;
int iReturn;
if ( *pbFail )
return FALSE;
if (wstrcmpEx(pNewValue, pOldValue, bCaseSensitive)) {
*pbIdentical = FALSE;
if ( pNewValue && *pNewValue ) {
if ( !(*ppString = AllocSplStr(pNewValue)) ) {
*pbFail = TRUE;
bReturn = FALSE;
}
} else {
*ppString = NULL;
}
}
return bReturn;
}
/*++
Routine Name:
AllocOrUpdateString
Routine Description:
This routine can be used to do an atomic update of values in a structure.
Create a temporary structure and copy the old structure to it.
Then call this routine for all LPWSTR fields to check and update strings
If the value changes:
This routine will allocate memory and assign pointer in the
temporary structure.
Arguments:
ppString : Points to a pointer in the temporary sturucture
pNewValue : New value to be set
pOldValue : Value in the original strucutre
bCaseSensitive : Determines whether case-sensitive string compare is performed
pbFail : On error set this to TRUE (Note: it could already be TRUE)
Return Value:
TRUE on success, else FALSE
--*/
BOOL
AllocOrUpdateString(
IN LPWSTR *ppString,
IN LPCWSTR pNewValue,
IN LPCWSTR pOldValue,
IN BOOL bCaseSensitive,
IN OUT BOOL *pbFail
)
{
BOOL bIdentical = FALSE;
return AllocOrUpdateStringAndTestSame(ppString, pNewValue, pOldValue, bCaseSensitive, pbFail, &bIdentical);
}
VOID
CopyNewOffsets(
LPBYTE pStruct,
LPBYTE pTempStruct,
LPDWORD lpOffsets)
/*++
Routine Description:
This routine can be used to do an atomic update of values in a structure.
Create a temporary structure and allocate memory for values which
are being updated in it, and set the remaining pointers to those in
the original.
This routine is called at the end to update the structure.
Arguments:
pStruct: Pointer to the structure
pTempStruct: Pointer to the temporary structure
lpOffsets: An array of DWORDS givings offsets within the stuctures
Return Value:
nothing
History:
MuhuntS -- Aug 95
--*/
{
register INT i;
for( i=0; lpOffsets[i] != 0xFFFFFFFF; ++i ) {
if ( *(LPBYTE *) (pStruct+lpOffsets[i]) !=
*(LPBYTE *) (pTempStruct+lpOffsets[i]) ) {
if ( *(LPBYTE *) (pStruct+lpOffsets[i]) )
FreeSplMem(*(LPBYTE *) (pStruct+lpOffsets[i]));
*(LPBYTE *) (pStruct+lpOffsets[i]) = *(LPBYTE *) (pTempStruct+lpOffsets[i]);
}
}
}
DWORD
GetIniDriverAndDirForThisMachine(
IN PINIPRINTER pIniPrinter,
OUT LPWSTR pszDriverDir,
OUT PINIDRIVER *ppIniDriver
)
/*++
Description:
Gets the path to the driver directory for the printer on the local machine
Arguments:
pIniPrinter - Points to IniPrinter
pszDriverDir - A buffer of size MAX_PATH to get the directory path
Return Vlaue:
Number of characters copied (0 on failure)
--*/
{
PINIVERSION pIniVersion = NULL;
PINIENVIRONMENT pIniEnvironment;
PINISPOOLER pIniSpooler = pIniPrinter->pIniSpooler;
DWORD dwIndex;
EnterSplSem();
//
// Find driver file for the given driver and then get it's fullpath
//
SPLASSERT(pIniPrinter && pIniPrinter->pIniDriver && pIniPrinter->pIniDriver->pName);
pIniEnvironment = FindEnvironment(szEnvironment, pIniSpooler);
*ppIniDriver = FindCompatibleDriver(pIniEnvironment,
&pIniVersion,
pIniPrinter->pIniDriver->pName,
dwMajorVersion,
dwUpgradeFlag);
SPLASSERT(*ppIniDriver);
dwIndex = GetDriverVersionDirectory(pszDriverDir,
MAX_PATH - 2,
pIniPrinter->pIniSpooler,
pIniEnvironment,
pIniVersion,
*ppIniDriver,
NULL);
pszDriverDir[dwIndex++] = L'\\';
pszDriverDir[dwIndex] = L'\0';
LeaveSplSem();
return dwIndex;
}
LPWSTR
GetConfigFilePath(
IN PINIPRINTER pIniPrinter
)
/*++
Description:
Gets the full path to the config file (driver ui file) associated with the
driver. Memory is allocated
Arguments:
pIniPrinter - Points to IniPrinter
Return Vlaue:
Pointer to the printer name buffer (NULL on error)
--*/
{
DWORD dwIndex;
WCHAR szDriverPath[MAX_PATH + 1];
PINIDRIVER pIniDriver;
if ( dwIndex = GetIniDriverAndDirForThisMachine(pIniPrinter,
szDriverPath,
&pIniDriver) )
wcscpy(szDriverPath+dwIndex, pIniDriver->pConfigFile);
return AllocSplStr(szDriverPath);
}
PDEVMODE
ConvertDevModeToSpecifiedVersion(
IN PINIPRINTER pIniPrinter,
IN PDEVMODE pDevMode,
IN LPWSTR pszConfigFile, OPTIONAL
IN LPWSTR pszPrinterNameWithToken, OPTIONAL
IN BOOL bNt35xVersion
)
/*++
Description:
Calls driver UI routines to get the default devmode and then converts given devmode
to that version. If the input devmode is in IniPrinter routine makes a copy before
converting it.
This routine needs to be called from inside spooler semaphore
Arguments:
pIniPrinter - Points to IniPrinter
pDevMode - Devmode to convert to current version
pConfigFile - Full path to driver UI file to do LoadLibrary (optional)
pszPrinterNameWithToken - Name of printer with token (optional)
bToNt3xVersion - If TRUE devmode is converted to Nt3x format, else to current version
Return Vlaue:
Pointer to new devmode on success, NULL on failure
--*/
{
LPWSTR pszLocalConfigFile = pszConfigFile;
LPWSTR pszLocalPrinterNameWithToken = pszPrinterNameWithToken;
LPDEVMODE pNewDevMode = NULL, pOldDevMode = NULL;
DWORD dwNeeded, dwLastError;
LONG lNeeded;
HANDLE hDevModeConvert = NULL,hPrinter = NULL;
BOOL bCallDocumentProperties = FALSE;
SplInSem();
//
// If ConfigFile or PrinterNameWithToken is not given allocate it locally
//
if ( !pszLocalConfigFile ) {
pszLocalConfigFile = GetConfigFilePath(pIniPrinter);
if ( !pszLocalConfigFile )
goto Cleanup;
}
if ( !pszLocalPrinterNameWithToken ) {
pszLocalPrinterNameWithToken = pszGetPrinterName( pIniPrinter,
TRUE,
pszLocalOnlyToken );
if ( !pszLocalPrinterNameWithToken )
goto Cleanup;
}
//
// If we are trying to convert pIniPrinter->pDevMode make a copy since we are going to leave SplSem
//
if ( pDevMode ) {
if ( pDevMode == pIniPrinter->pDevMode ) {
dwNeeded = pDevMode->dmSize + pDevMode->dmDriverExtra;
SPLASSERT(dwNeeded == pIniPrinter->cbDevMode);
pOldDevMode = AllocSplMem(dwNeeded);
if ( !pOldDevMode )
goto Cleanup;
CopyMemory((LPBYTE)pOldDevMode, (LPBYTE)pDevMode, dwNeeded);
} else {
pOldDevMode = pDevMode;
}
}
//
// Driver is going to call OpenPrinter, so leave SplSem
//
LeaveSplSem();
SplOutSem();
hDevModeConvert = LoadDriverFiletoConvertDevmode(pszLocalConfigFile);
if ( !hDevModeConvert ) {
// If the function is not exported and 3.5x conversion is not required
// the devmode can be got from DocumentProperties
if ( bNt35xVersion != NT3X_VERSION ) {
bCallDocumentProperties = TRUE;
}
goto CleanupFromOutsideSplSem;
}
dwNeeded = 0;
if ( bNt35xVersion == NT3X_VERSION ) {
//
// Call CallDrvDevModeConversion to allocate memory and return 351 devmode
//
dwLastError = CallDrvDevModeConversion(hDevModeConvert,
pszLocalPrinterNameWithToken,
(LPBYTE)pOldDevMode,
(LPBYTE *)&pNewDevMode,
&dwNeeded,
CDM_CONVERT351,
TRUE);
SPLASSERT(dwLastError == ERROR_SUCCESS || !pNewDevMode);
} else {
//
// Call CallDrvDevModeConversion to allocate memory and give default devmode
dwLastError = CallDrvDevModeConversion(hDevModeConvert,
pszLocalPrinterNameWithToken,
NULL,
(LPBYTE *)&pNewDevMode,
&dwNeeded,
CDM_DRIVER_DEFAULT,
TRUE);
if ( dwLastError != ERROR_SUCCESS ) {
SPLASSERT(!pNewDevMode);
// Call DocumentProperties to get the default devmode
bCallDocumentProperties = TRUE;
goto CleanupFromOutsideSplSem;
}
//
// If we have an input devmode to convert to current mode call driver again
//
if ( pOldDevMode ) {
dwLastError = CallDrvDevModeConversion(hDevModeConvert,
pszLocalPrinterNameWithToken,
(LPBYTE)pOldDevMode,
(LPBYTE *)&pNewDevMode,
&dwNeeded,
CDM_CONVERT,
FALSE);
//
// If call failed free devmode which was allocated by previous call
//
if ( dwLastError != ERROR_SUCCESS ) {
// Call DocumentProperties to get the default devmode
bCallDocumentProperties = TRUE;
goto CleanupFromOutsideSplSem;
}
}
}
CleanupFromOutsideSplSem:
if (bCallDocumentProperties) {
// Get a client side printer handle to pass to the driver
if (!(* pfnOpenPrinter)(pszLocalPrinterNameWithToken, &hPrinter, NULL)) {
goto ReEnterSplSem;
}
if (!pNewDevMode) {
// Get the default devmode
lNeeded = (* pfnDocumentProperties)(NULL,
hPrinter,
pszLocalPrinterNameWithToken,
NULL,
NULL,
0);
if (lNeeded <= 0 ||
!(pNewDevMode = (LPDEVMODEW) AllocSplMem(lNeeded)) ||
(* pfnDocumentProperties)(NULL,
hPrinter,
pszLocalPrinterNameWithToken,
pNewDevMode,
NULL,
DM_OUT_BUFFER) < 0) {
if (pNewDevMode) {
FreeSplMem(pNewDevMode);
pNewDevMode = NULL;
goto ReEnterSplSem;
}
}
}
if (pOldDevMode) {
// Convert to Current mode
if ((* pfnDocumentProperties)(NULL,
hPrinter,
pszLocalPrinterNameWithToken,
pNewDevMode,
pOldDevMode,
DM_IN_BUFFER | DM_OUT_BUFFER) < 0) {
FreeSplMem(pNewDevMode);
pNewDevMode = NULL;
goto ReEnterSplSem;
}
}
}
ReEnterSplSem:
if (hPrinter) {
(* pfnClosePrinter)(hPrinter);
}
SplOutSem();
EnterSplSem();
Cleanup:
if ( hDevModeConvert )
UnloadDriverFile(hDevModeConvert);
if ( pszLocalConfigFile != pszConfigFile )
FreeSplStr(pszLocalConfigFile);
if ( pszPrinterNameWithToken != pszLocalPrinterNameWithToken )
FreeSplStr(pszLocalPrinterNameWithToken);
if ( pOldDevMode != pDevMode )
FreeSplMem(pOldDevMode);
return pNewDevMode;
}
BOOL
IsPortType(
LPWSTR pPort,
LPWSTR pPrefix
)
{
DWORD dwLen;
SPLASSERT(pPort && *pPort && pPrefix && *pPrefix);
dwLen = wcslen(pPrefix);
if ( wcslen(pPort) < dwLen ) {
return FALSE;
}
if ( _wcsnicmp(pPort, pPrefix, dwLen) )
{
return FALSE;
}
//
// wcslen guarenteed >= 3
//
return pPort[ wcslen( pPort ) - 1 ] == L':';
}
/* UnicodeToAnsiString
*
* Parameters:
*
* pUnicode - A valid source Unicode string.
*
* pANSI - A pointer to a buffer large enough to accommodate
* the converted string.
*
* StringLength - The length of the source Unicode string.
* If 0 (NULL_TERMINATED), the string is assumed to be
* null-terminated.
*
*
* Notes:
* With DBCS enabled, we will allocate twice the size of the
* buffer including the null terminator to take care of double
* byte character strings - KrishnaG
*
* pUnicode is truncated to StringLength characters.
*
* Return:
*
* The return value from WideCharToMultiByte, the number of
* multi-byte characters returned.
*
*
* andrewbe, 11 Jan 1993
*/
INT
UnicodeToAnsiString(
LPWSTR pUnicode,
LPSTR pAnsi,
DWORD StringLength)
{
LPSTR pTempBuf = NULL;
INT rc = 0;
if( StringLength == NULL_TERMINATED ) {
//
// StringLength is just the
// number of characters in the string
//
StringLength = wcslen( pUnicode );
}
//
// WideCharToMultiByte doesn't NULL terminate if we're copying
// just part of the string, so terminate here.
//
pUnicode[StringLength] = 0;
//
// Include one for the NULL
//
StringLength++;
//
// Unfortunately, WideCharToMultiByte doesn't do conversion in place,
// so allocate a temporary buffer, which we can then copy:
//
if( pAnsi == (LPSTR)pUnicode )
{
pTempBuf = AllocSplMem(StringLength*2);
pAnsi = pTempBuf;
}
if( pAnsi )
{
rc = WideCharToMultiByte( CP_THREAD_ACP,
0,
pUnicode,
StringLength,
pAnsi,
StringLength*2,
NULL,
NULL );
}
/* If pTempBuf is non-null, we must copy the resulting string
* so that it looks as if we did it in place:
*/
if( pTempBuf )
{
if( rc > 0 )
{
pAnsi = (LPSTR)pUnicode;
strcpy( pAnsi, pTempBuf );
}
FreeSplMem(pTempBuf);
}
return rc;
}
LPWSTR
AnsiToUnicodeStringWithAlloc(
LPSTR pAnsi
)
/*++
Description:
Convert ANSI string to UNICODE. Routine allocates memory from the heap
which should be freed by the caller.
Arguments:
pAnsi - Points to the ANSI string
Return Vlaue:
Pointer to UNICODE string
--*/
{
LPWSTR pUnicode;
DWORD rc;
rc = MultiByteToWideChar(CP_ACP,
MB_PRECOMPOSED,
pAnsi,
-1,
NULL,
0);
rc *= sizeof(WCHAR);
if ( !rc || !(pUnicode = (LPWSTR) AllocSplMem(rc)) )
return NULL;
rc = MultiByteToWideChar(CP_ACP,
MB_PRECOMPOSED,
pAnsi,
-1,
pUnicode,
rc);
if ( rc )
return pUnicode;
else {
FreeSplMem(pUnicode);
return NULL;
}
}
VOID
FreeIniSpoolerOtherNames(
PINISPOOLER pIniSpooler
)
{
DWORD Index = 0;
if ( pIniSpooler->ppszOtherNames ) {
for ( Index = 0 ; Index < pIniSpooler->cOtherNames ; ++Index ) {
FreeSplMem(pIniSpooler->ppszOtherNames[Index]);
}
FreeSplMem(pIniSpooler->ppszOtherNames);
}
pIniSpooler->cOtherNames = 0;
}
BOOL
SplMonitorIsInstalled(
LPWSTR pMonitorName
)
{
BOOL bRet;
EnterSplSem();
bRet = FindMonitor(pMonitorName, pLocalIniSpooler) != NULL;
LeaveSplSem();
return bRet;
}
BOOL
PrinterDriverEvent(
PINIPRINTER pIniPrinter,
INT PrinterEvent,
LPARAM lParam
)
/*++
--*/
{
BOOL ReturnValue = FALSE;
LPWSTR pPrinterName = NULL;
BOOL InSpoolSem = TRUE;
SplOutSem();
EnterSplSem();
//
// We have to Clone the name string, incase someone does a
// rename whilst outside criticalsection.
//
pPrinterName = pszGetPrinterName( pIniPrinter, TRUE, pszLocalsplOnlyToken);
LeaveSplSem();
SplOutSem();
if ( (pIniPrinter->pIniSpooler->SpoolerFlags & SPL_PRINTER_DRIVER_EVENT) &&
pPrinterName != NULL ) {
ReturnValue = SplDriverEvent( pPrinterName, PrinterEvent, lParam );
}
FreeSplStr( pPrinterName );
return ReturnValue;
}
BOOL
SplDriverEvent(
LPWSTR pName,
INT PrinterEvent,
LPARAM lParam
)
{
BOOL ReturnValue = FALSE;
if ( pfnPrinterEvent != NULL ) {
SplOutSem();
SPLASSERT( pName && PrinterEvent );
DBGMSG(DBG_INFO, ("SplDriverEvent %ws %d %x\n", pName, PrinterEvent, lParam));
ReturnValue = (*pfnPrinterEvent)( pName, PrinterEvent, PRINTER_EVENT_FLAG_NO_UI, lParam );
}
return ReturnValue;
}
DWORD
OpenPrinterKey(
PINIPRINTER pIniPrinter,
REGSAM samDesired,
HANDLE *phKey,
LPCWSTR pKeyName,
BOOL bOpen
)
/*++
Description: OpenPrinterKey
Opens "Printers" and IniPrinter->pName keys, then opens
specified subkey "pKeyName" if pKeyName is not NULL
This routine needs to be called from inside spooler semaphore
Arguments:
pIniPrinter - Points to IniPrinter
*phPrinterRootKey - Handle to "Printers" key on return
*phPrinterKey - Handle to IniPrinter->pName key on return
*hKey - Handle to pKeyName key on return
pKeyName - Points to SubKey to open
Return Value:
Success or failure status
Author: Steve Wilson (NT)
--*/
{
LPWSTR pThisKeyName = NULL;
LPWSTR pPrinterKeyName = NULL;
DWORD rc;
PINISPOOLER pIniSpooler = pIniPrinter->pIniSpooler;
SplInSem();
if (!(pPrinterKeyName = SubChar(pIniPrinter->pName, L'\\', L','))) {
rc = GetLastError();
goto error;
}
if (pKeyName && *pKeyName){
pThisKeyName = AllocSplMem(( wcslen(pPrinterKeyName) +
wcslen(pKeyName) + 2 ) * sizeof(WCHAR) );
if (!pThisKeyName) {
rc = GetLastError();
goto error;
}
wsprintf(pThisKeyName, L"%ws\\%ws", pPrinterKeyName, pKeyName);
} else {
pThisKeyName = pPrinterKeyName;
}
if (bOpen) { // Open
rc = SplRegOpenKey( pIniSpooler->hckPrinters,
pThisKeyName,
samDesired,
phKey,
pIniSpooler );
}
else { // Create
rc = SplRegCreateKey( pIniSpooler->hckPrinters,
pThisKeyName,
0,
samDesired,
NULL,
phKey,
NULL,
pIniSpooler );
}
error:
if (pThisKeyName != pPrinterKeyName) {
FreeSplMem(pThisKeyName);
}
FreeSplStr(pPrinterKeyName);
return rc;
}
BOOL
SplGetDriverDir(
HANDLE hIniSpooler,
LPWSTR pszDir,
LPDWORD pcchDir
)
{
DWORD dwSize;
PINISPOOLER pIniSpooler = (PINISPOOLER)hIniSpooler;
SPLASSERT(pIniSpooler && pIniSpooler->signature == ISP_SIGNATURE);
dwSize = *pcchDir;
*pcchDir = wcslen(pIniSpooler->pDir) + wcslen(szDriverDir) + 2;
if ( *pcchDir > dwSize ) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
wsprintf(pszDir, L"%ws\\%ws", pIniSpooler->pDir, szDriverDir);
return TRUE;
}
VOID
GetRegistryLocation(
IN HANDLE hKey,
IN LPCWSTR pszPath,
OUT PHANDLE phKeyOut,
OUT LPCWSTR *ppszPathOut
)
/*++
Routine Description:
Take a registry path and detect whether it should be absolute (rooted
from HKEY_LOCAL_MACHINE), or if it should be branched off of the
subkey that is passed in hKey.
By convention, if it starts with "\" then it is an absolute path.
Otherwise it is relative off hKey.
Arguments:
hKey - Print hKey
pszPath - Path to parse. If the pszPath starts with a
backslash, then it is absolute.
phKeyOut - New key that should be used.
ppszPathOut - New path that should be used.
Return Value:
--*/
{
if( pszPath && ( pszPath[0] == TEXT( '\\' ))){
*phKeyOut = HKEY_LOCAL_MACHINE;
*ppszPathOut = &pszPath[1];
return;
}
*phKeyOut = hKey;
*ppszPathOut = pszPath;
}
DWORD
DeletePrinterSubkey(
PINIPRINTER pIniPrinter,
PWSTR pKeyName
)
{
HKEY hKey, hPrinterKey;
DWORD dwError;
dwError = OpenPrinterKey(pIniPrinter, KEY_WRITE | KEY_READ, &hPrinterKey, NULL, TRUE);
if (dwError == ERROR_SUCCESS) {
dwError = OpenPrinterKey( pIniPrinter,
KEY_WRITE | KEY_READ,
&hKey,
pKeyName,
TRUE);
if (dwError == ERROR_SUCCESS) {
dwError = SplDeleteThisKey( hPrinterKey,
hKey,
pKeyName,
FALSE,
pIniPrinter->pIniSpooler);
}
SplRegCloseKey(hPrinterKey, pIniPrinter->pIniSpooler);
}
return dwError;
}
PWSTR
FixDelim(
PCWSTR pszInBuffer,
WCHAR wcDelim
)
/*++
Routine Description:
Removes duplicate delimiters from a set of delimited strings
Arguments:
pszInBuffer - Input list of comma delimited strings
wcDelim - The delimit character
Return Value:
Returns a fixed string
--*/
{
PWSTR pszIn, pszOut, pszOutBuffer;
BOOL bFoundDelim = TRUE;
pszOutBuffer = (PWSTR) AllocSplMem((wcslen(pszInBuffer) + 1)*sizeof(WCHAR));
if (pszOutBuffer) {
for(pszOut = pszOutBuffer, pszIn = (PWSTR) pszInBuffer ; *pszIn ; ++pszIn) {
if (*pszIn == wcDelim) {
if (!bFoundDelim) {
bFoundDelim = TRUE;
*pszOut++ = *pszIn;
}
} else {
bFoundDelim = FALSE;
*pszOut++ = *pszIn;
}
}
// Check for trailing delimiter
if (pszOut != pszOutBuffer && *(pszOut - 1) == wcDelim) {
*(pszOut - 1) = L'\0';
}
*pszOut = L'\0';
}
return pszOutBuffer;
}
PWSTR
Array2DelimString(
PSTRINGS pStringArray,
WCHAR wcDelim
)
/*++
Routine Description:
Converts a PSTRINGS structure to a set of delimited strings
Arguments:
pStringArray - Input PSTRINGS structure
wcDelim - The delimit character
Return Value:
Delimited string buffer
--*/
{
DWORD i, nBytes;
PWSTR pszDelimString;
WCHAR szDelimString[2];
if (!pStringArray || pStringArray->nElements == 0)
return NULL;
szDelimString[0] = wcDelim;
szDelimString[1] = L'\0';
// Determine memory requirement
for (i = nBytes = 0 ; i < pStringArray->nElements ; ++i) {
//
// allocate extra space for the delimiter
//
if (pStringArray->ppszString[i])
nBytes += (wcslen(pStringArray->ppszString[i]) + 1)*sizeof (WCHAR);
}
pszDelimString = (PWSTR) AllocSplMem(nBytes);
if (!pszDelimString)
return NULL;
for (i = 0 ; i < pStringArray->nElements - 1 ; ++i) {
if (pStringArray->ppszString[i]) {
wcscat(pszDelimString, pStringArray->ppszString[i]);
wcscat(pszDelimString, szDelimString);
}
}
if (pStringArray->ppszString[i])
wcscat(pszDelimString, pStringArray->ppszString[i]);
return pszDelimString;
}
PSTRINGS
ShortNameArray2LongNameArray(
PSTRINGS pShortNames
)
/*++
Routine Description:
Converts a PSTRINGS structure containing short names to a PSTRINGS struct containing the dns
equivalents
Arguments:
pStringArray - Input PSTRINGS structure
Return Value:
PSTRINGS struct containing the dns equivalents of the input short name PSTRINGS struct.
--*/
{
PSTRINGS pLongNames;
DWORD i;
if (!pShortNames) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
// Allocate LongNameArray
pLongNames = AllocStringArray(pShortNames->nElements);
if (!pLongNames)
return NULL;
for (i = 0 ; i < pShortNames->nElements ; ++i) {
// GetDNSMachineName may fail, leaving the LongNameArray element empty. This is okay.
GetDNSMachineName(pShortNames->ppszString[i], &pLongNames->ppszString[i]);
}
pLongNames->nElements = pShortNames->nElements;
return pLongNames;
}
PSTRINGS
DelimString2Array(
PCWSTR pszDelimString,
WCHAR wcDelim
)
/*++
Routine Description:
Converts a delimited string to a PSTRINGS structure
Arguments:
pszDelimString - Input, delimited strings
wcDelim - The delimit character
Return Value:
PSTRINGS structure
--*/
{
PWSTR psz, pszDelim;
PSTRINGS pStrings = NULL;
ULONG i, cChar, nStrings;
// Get number of names
for (psz = (PWSTR) pszDelimString, nStrings = 0 ; psz++ ; psz = wcschr(psz, wcDelim))
++nStrings;
pStrings = AllocStringArray(nStrings);
if (!pStrings)
goto error;
// Copy delimited string to array
for (i = 0, psz = (PWSTR) pszDelimString ; i < nStrings && psz ; ++i, psz = pszDelim + 1) {
pszDelim = wcschr(psz, wcDelim);
if (pszDelim) {
cChar = (ULONG) (pszDelim - psz);
pStrings->ppszString[i] = (PWSTR) AllocSplMem((cChar + 1)*sizeof(WCHAR));
} else {
pStrings->ppszString[i] = (PWSTR) AllocSplMem((wcslen(psz) + 1)*sizeof(WCHAR));
}
if (!pStrings->ppszString[i]) {
pStrings->nElements = i;
FreeStringArray(pStrings);
pStrings = NULL;
goto error;
}
if (pszDelim) {
wcsncpy(pStrings->ppszString[i], psz, cChar);
*(pStrings->ppszString[i] + cChar) = L'\0';
} else {
wcscpy(pStrings->ppszString[i], psz);
}
}
pStrings->nElements = nStrings;
error:
return pStrings;
}
VOID
FreeStringArray(
PSTRINGS pString
)
/*++
Routine Description:
Frees a PSTRINGS structure
Arguments:
pString - PSTRINGS structure to free
Return Value:
--*/
{
DWORD i;
if (pString) {
for (i = 0 ; i < pString->nElements ; ++i) {
if (pString->ppszString[i])
FreeSplMem(pString->ppszString[i]);
}
FreeSplMem(pString);
}
}
PSTRINGS
AllocStringArray(
DWORD nStrings
)
/*++
Routine Description:
Allocates a PSTRINGS structure
Arguments:
nStrings - number of strings in the structure
Return Value:
pointer to allocated PSTRINGS structure, if any
--*/
{
PSTRINGS pStrings;
// Allocate the STRINGS struct
pStrings = (PSTRINGS) AllocSplMem(sizeof(STRINGS) + (nStrings - 1)*sizeof *pStrings->ppszString);
return pStrings;
}
BOOL
SplDeleteFile(
LPCTSTR lpFileName
)
/*++
Routine Name
SplDeleteFile
Routine Description:
Removes SFP protection and Deletes a file.
If the file is protected and I fail to remove protection,
the user will get warned with a system pop up.
Arguments:
lpFileName - file full path requested
Return Value:
DeleteFile's return value
--*/
{
HANDLE RpcHandle = INVALID_HANDLE_VALUE;
RpcHandle = SfcConnectToServer( NULL );
if( RpcHandle != INVALID_HANDLE_VALUE ){
SfcFileException( RpcHandle,
(PWSTR)lpFileName,
FILE_ACTION_REMOVED
);
SfcClose(RpcHandle);
}
//
// SfcFileException might fail with ERROR_FILE_NOT_FOUND because the file is
// not in the protected file list.That's why I call DeleteFile anyway.
//
return DeleteFile( lpFileName );
}
BOOL
SplMoveFileEx(
LPCTSTR lpExistingFileName,
LPCTSTR lpNewFileName,
DWORD dwFlags
)
/*++
Routine Name
SplMoveFileEx
Routine Description:
Removes SFP protection and move a file;
If the file is protected and I fail to remove protection,
the user will get warned with a system pop up.
Arguments:
lpExistingFileName - pointer to the name of the existing file
lpNewFileName - pointer to the new name for the file
dwFlags - flag that specifies how to move file
Return Value:
MoveFileEx's return value
--*/
{
HANDLE RpcHandle = INVALID_HANDLE_VALUE;
RpcHandle = SfcConnectToServer( NULL );
if( RpcHandle != INVALID_HANDLE_VALUE ){
SfcFileException( RpcHandle,
(PWSTR)lpExistingFileName,
FILE_ACTION_REMOVED
);
SfcClose(RpcHandle);
}
//
// SfcFileException might fail with ERROR_FILE_NOT_FOUND because the file is
// not in the protected file list.That's why I call MoveFileEx anyway.
//
return MoveFileEx( lpExistingFileName, lpNewFileName, dwFlags );
}
BOOL
GetSpoolerPolicy(
PWSTR pszValue,
PBYTE pData,
PDWORD pcbData
)
{
HKEY hPrintPublishKey = NULL;
DWORD dwType, dwData, cbData;
BOOL bReturn = FALSE;
// NOTE: This fcn currently only works if type is REG_DWORD. Expand fcn as needed.
cbData = sizeof(DWORD);
// Open the registry key
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
szPrintPublishPolicy,
0,
KEY_READ,
&hPrintPublishKey) == ERROR_SUCCESS) {
// Query for the policy bits
if (RegQueryValueEx(hPrintPublishKey,
pszValue,
NULL,
&dwType,
(LPBYTE) pData,
pcbData) == ERROR_SUCCESS)
bReturn = TRUE;
RegCloseKey(hPrintPublishKey);
}
return bReturn;
}
DWORD
GetDwPolicy(
PWSTR pszName,
DWORD dwDefault
)
{
DWORD dwData, cbData = sizeof(DWORD);
return GetSpoolerPolicy(pszName, (PBYTE) &dwData, &cbData) ? dwData : dwDefault;
}
DWORD
GetDefaultForKMPrintersBlockedPolicy (
)
{
DWORD Default = KM_PRINTERS_ARE_BLOCKED;
BOOL bIsNTWorkstation;
NT_PRODUCT_TYPE NtProductType;
//
// DEFAULT_KM_PRINTERS_ARE_BLOCKED is "blocked"
//
if ( RtlGetNtProductType(&NtProductType) ) {
bIsNTWorkstation = NtProductType == NtProductWinNt;
Default = bIsNTWorkstation ?
WKS_DEFAULT_KM_PRINTERS_ARE_BLOCKED :
SERVER_DEFAULT_KM_PRINTERS_ARE_BLOCKED;
}
return Default;
}
DWORD
GetServerInstallTimeOut(
)
{
HKEY hKey;
DWORD dwDummy;
DWORD dwTimeOut = DEFAULT_MAX_TIMEOUT;
DWORD dwSize = sizeof(dwTimeOut);
LPCWSTR cszPrintKey = L"SYSTEM\\CurrentControlSet\\Control\\Print";
LPCWSTR cszTimeOut = L"ServerInstallTimeOut";
if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, cszPrintKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS )
{
if(RegQueryValueEx( hKey, cszTimeOut, 0, &dwDummy, (LPBYTE)&dwTimeOut, &dwSize ) != ERROR_SUCCESS)
{
dwTimeOut = DEFAULT_MAX_TIMEOUT;
}
RegCloseKey( hKey );
}
return dwTimeOut;
}
DWORD
ReadOverlapped( HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead )
{
DWORD dwLastError = ERROR_SUCCESS;
if( hFile != INVALID_HANDLE_VALUE )
{
OVERLAPPED Ov;
ZeroMemory( &Ov,sizeof(Ov));
if ( !(Ov.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) )
{
dwLastError = GetLastError();
goto Cleanup;
}
if( !ReadFile( hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, &Ov ) &&
GetLastError() != ERROR_IO_PENDING )
{
dwLastError = GetLastError();
goto Cleanup;
}
if( WaitForSingleObject(Ov.hEvent, gdwServerInstallTimeOut) == WAIT_TIMEOUT )
{
CancelIo(hFile);
WaitForSingleObject(Ov.hEvent, INFINITE);
}
if( !GetOverlappedResult(hFile, &Ov, lpNumberOfBytesRead, FALSE) )
dwLastError = GetLastError();
Cleanup:
if ( Ov.hEvent )
CloseHandle(Ov.hEvent);
}
else
{
dwLastError = ERROR_INVALID_HANDLE;
SetLastError( dwLastError );
}
return dwLastError;
}
BOOL
WriteOverlapped( HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead )
{
DWORD dwLastError = ERROR_SUCCESS;
if( hFile != INVALID_HANDLE_VALUE )
{
OVERLAPPED Ov;
ZeroMemory( &Ov,sizeof(Ov));
if ( !(Ov.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) )
{
dwLastError = GetLastError();
goto Cleanup;
}
if( !WriteFile( hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, &Ov ) &&
GetLastError() != ERROR_IO_PENDING )
{
dwLastError = GetLastError();
goto Cleanup;
}
if( WaitForSingleObject(Ov.hEvent, gdwServerInstallTimeOut) == WAIT_TIMEOUT )
{
CancelIo(hFile);
WaitForSingleObject(Ov.hEvent, INFINITE);
}
if( !GetOverlappedResult(hFile, &Ov, lpNumberOfBytesRead, FALSE) )
dwLastError = GetLastError();
Cleanup:
if ( Ov.hEvent )
CloseHandle(Ov.hEvent);
}
else
{
dwLastError = ERROR_INVALID_HANDLE;
SetLastError(dwLastError);
}
return (dwLastError == ERROR_SUCCESS);
}
ULONG_PTR
AlignToRegType(
IN ULONG_PTR Data,
IN DWORD RegType
)
{
//
// Alings the value if Data to the boundary
// dictated by the type of data read from registry.
//
ULONG_PTR Boundary;
switch ( RegType )
{
//
// Binary data could store any kind of data. The pointer is casted
// to LPDWORD or LPBOOL so make sure it is native aligned.
//
case REG_BINARY:
{
Boundary = sizeof(ULONG_PTR);
}
break;
case REG_SZ:
case REG_EXPAND_SZ:
case REG_MULTI_SZ:
{
Boundary = sizeof(WCHAR);
}
break;
case REG_DWORD:
case REG_DWORD_BIG_ENDIAN:
{
Boundary = sizeof(DWORD32);
}
break;
case REG_QWORD:
{
Boundary = sizeof(DWORD64);
}
break;
case REG_NONE:
default:
{
Boundary = sizeof(ULONG_PTR);
}
}
return (Data + (Boundary - 1))&~(Boundary - 1);
}
/*++
Routine Name
BuildAclStruct
Routine Description:
Helper function. Builds a vector of ACEs to allow all
access to administrators and system. The caller has to
free the pstrName fields
Arguments:
cElements - number of elements in the array
pExplAcc - vector of aces
Return Value:
WIN32 error code
--*/
DWORD
BuildAclStruct(
IN DWORD cElements,
IN OUT EXPLICIT_ACCESS *pExplAcc
)
{
DWORD dwError = ERROR_INVALID_PARAMETER;
if (pExplAcc && cElements==2)
{
PSID pAdminSid = NULL;
PSID pSystemSid = NULL;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
//
// Get SID for the built in system account
//
dwError = AllocateAndInitializeSid(&NtAuthority,
1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&pSystemSid) &&
AllocateAndInitializeSid(&NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pAdminSid) ? ERROR_SUCCESS : GetLastError();
if (dwError == ERROR_SUCCESS)
{
//
// Initialize the EXPLICIT_ACCESS with information about administrators
//
pExplAcc[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
pExplAcc[0].Trustee.pMultipleTrustee = NULL;
pExplAcc[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
pExplAcc[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
pExplAcc[0].Trustee.ptstrName = (PTSTR)pAdminSid;
pExplAcc[0].grfAccessMode = GRANT_ACCESS;
pExplAcc[0].grfAccessPermissions = GENERIC_ALL;
pExplAcc[0].grfInheritance = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
//
// Initialize the EXPLICIT_ACCESS with information about the system
//
pExplAcc[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
pExplAcc[1].Trustee.pMultipleTrustee = NULL;
pExplAcc[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
pExplAcc[1].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
pExplAcc[1].Trustee.ptstrName = (PTSTR)pSystemSid;
pExplAcc[1].grfAccessMode = GRANT_ACCESS;
pExplAcc[1].grfAccessPermissions = GENERIC_ALL;
pExplAcc[1].grfInheritance = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
}
else
{
//
// Note that we never end up here and have pAdminSid not NULL. However, for the
// sake of consisentcy and extensibility we attempt to clean up both strcutures
//
if (pSystemSid) FreeSid(pSystemSid);
if (pAdminSid) FreeSid(pAdminSid);
}
}
return dwError;
}
/*++
Routine Name
CreateProtectedDirectory
Routine Description:
Creates a directory with full access only to admins and to
the system. Contained objects inherit these permissions.
Arguments:
pszDir - directory name
Return Value:
WIN32 error code
--*/
DWORD
CreateProtectedDirectory(
IN LPCWSTR pszDir
)
{
DWORD dwError = ERROR_INVALID_PARAMETER;
if (pszDir)
{
SECURITY_DESCRIPTOR SecDesc;
SECURITY_ATTRIBUTES SecAttr;
PACL pDacl = NULL;
EXPLICIT_ACCESS ExplicitAccessVector[2] = {0};
if ((dwError = InitializeSecurityDescriptor(&SecDesc,
SECURITY_DESCRIPTOR_REVISION) ?
ERROR_SUCCESS : GetLastError()) == ERROR_SUCCESS &&
//
// Initialize the ExplicitAccessVector
//
(dwError = BuildAclStruct(COUNTOF(ExplicitAccessVector),
ExplicitAccessVector)) == ERROR_SUCCESS &&
//
// Initialize the DACL
//
(dwError = SetEntriesInAcl(COUNTOF(ExplicitAccessVector),
ExplicitAccessVector,
NULL,
&pDacl)) == ERROR_SUCCESS &&
//
// Set the DACL in the security descriptor
//
(dwError = SetSecurityDescriptorDacl(&SecDesc,
TRUE,
pDacl,
FALSE) ? ERROR_SUCCESS : GetLastError()) == ERROR_SUCCESS &&
//
// Check if the security descriptor is valid. Function does not set last error
//
(dwError = IsValidSecurityDescriptor(&SecDesc) ?
ERROR_SUCCESS : ERROR_INVALID_SECURITY_DESCR) == ERROR_SUCCESS)
{
//
// Put the security descriptor in the security attribute
//
SecAttr.bInheritHandle = FALSE;
SecAttr.nLength = sizeof(SecAttr);
SecAttr.lpSecurityDescriptor = &SecDesc;
dwError = CreateDirectory(pszDir, &SecAttr) ? ERROR_SUCCESS : GetLastError();
}
//
// The ptstrName here points to a sid obtained via AllocAndInitializeSid
//
if (ExplicitAccessVector[0].Trustee.ptstrName)
{
FreeSid(ExplicitAccessVector[0].Trustee.ptstrName);
}
//
// The ptstrName here points to a sid obtained via AllocAndInitializeSid
//
if (ExplicitAccessVector[1].Trustee.ptstrName)
{
FreeSid((PSID)ExplicitAccessVector[1].Trustee.ptstrName);
}
}
DBGMSG(DBG_CLUSTER, ("CreateProtectedDirectory returns Win32 error %u\n", dwError));
return dwError;
}
/*++
Routine Name
CopyFileToDirectory
Routine Description:
Copies a file to a directory. File is fully qualified.
The function takes a pszDestDir and up to 3 directories.
It will create the dir: pszRoot\pszDir1\pszDir2\pszDir3
and copy the file over there. This is a helper function
for installing drivers on clusters. The directory strcuture
is created with special privileges. Only the system and
administrators have access to it.
Arguments:
pssDestDirt - destination directory
pszDir1 - optional
pszDir2 - optional
pszdir3 - optional
pszFullFileName - qualified file path
Return Value:
WIN32 error code
--*/
DWORD
CopyFileToDirectory(
IN LPCWSTR pszFullFileName,
IN LPCWSTR pszDestDir,
IN LPCWSTR pszDir1,
IN LPCWSTR pszDir2,
IN LPCWSTR pszDir3
)
{
DWORD dwError = ERROR_INVALID_PARAMETER;
LPWSTR pszFile = NULL;
//
// Our pszfullFileName must contain at least one "\"
//
if (pszFullFileName &&
pszDestDir &&
(pszFile = wcsrchr(pszFullFileName, L'\\')))
{
LPCWSTR ppszArray[] = {pszDir1, pszDir2, pszDir3};
WCHAR szNewPath[MAX_PATH];
DWORD uIndex;
DBGMSG(DBG_CLUSTER, ("CopyFileToDirectory\n\tpszFullFile "TSTR"\n\tpszDest "TSTR"\n\tDir1 "TSTR"\n\tDir2 "TSTR"\n\tDir3 "TSTR"\n",
pszFullFileName, pszDestDir, pszDir1, pszDir2, pszDir3));
//
// Prepare buffer for the loop (initialize to pszDestDir)
// Create destination root directory, if not existing
//
if ((dwError = StrNCatBuff(szNewPath,
MAX_PATH,
pszDestDir,
NULL)) == ERROR_SUCCESS &&
(DirectoryExists((LPWSTR)pszDestDir) ||
(dwError = CreateProtectedDirectory(pszDestDir)) == ERROR_SUCCESS))
{
for (uIndex = 0;
uIndex < COUNTOF(ppszArray) && dwError == ERROR_SUCCESS;
uIndex++)
{
//
// Append the first directory to the path and
// Create the directory if not existing
//
if (ppszArray[uIndex] &&
(dwError = StrNCatBuff(szNewPath,
MAX_PATH,
szNewPath,
L"\\",
ppszArray[uIndex],
NULL)) == ERROR_SUCCESS &&
!DirectoryExists(szNewPath) &&
!CreateDirectoryWithoutImpersonatingUser(szNewPath))
{
dwError = GetLastError();
}
}
//
// Create the destination file full name and copy the file
//
if (dwError == ERROR_SUCCESS &&
(dwError = StrNCatBuff(szNewPath,
MAX_PATH,
szNewPath,
pszFile,
NULL)) == ERROR_SUCCESS)
{
dwError = CopyFile(pszFullFileName, szNewPath, FALSE) ? ERROR_SUCCESS : GetLastError();
}
DBGMSG(DBG_CLUSTER, ("CopyFileToDirectory szNewPath "TSTR"\n", szNewPath));
}
}
DBGMSG(DBG_CLUSTER, ("CopyFileToDirectory returns Win32 error %u\n", dwError));
return dwError;
}
/*++
Routine Name
PropagateMonitorToCluster
Routine Description:
For a cluster we keep the following printing resources in the
cluster data base: drivers, printers, ports, procs. We can also
have cluster aware port monitors. When the spooler initializes
those objects, it reads the data from the cluster data base.
When we write those objects we pass a handle to a key to some
spooler functions. (Ex WriteDriverIni) The handle is either to the
local registry or to the cluster database. This procedure is not safe
with language monitors. LMs keep data in the registry so you need
to supply a handle to a key in the reg. They don't work with clusters.
This function will do this:
1) write an association key in the cluster data base. We will have
information like (lm name, dll name) (see below where we store this)
2) copy the lm dll to the cluster disk.
When we fail over we have all it take to install the lm on the local
node if needed.
Theoretically this function works for both language and port monitors.
But it is useless when applied to port monitors.
Arguments:
pszName - monitor name
pszDllName - dll name of the monitor
pszEnvName - envionment string Ex "Windows NT x86"
pszEnvDir - the path on disk for the environement Ex w32x86
pIniSpooler - cluster spooler
Return Value:
WIN32 error code
--*/
DWORD
PropagateMonitorToCluster(
IN LPCWSTR pszName,
IN LPCWSTR pszDLLName,
IN LPCWSTR pszEnvName,
IN LPCWSTR pszEnvDir,
IN PINISPOOLER pIniSpooler
)
{
DWORD dwError = ERROR_INVALID_PARAMETER;
HKEY hKeyEnvironments;
HKEY hKeyCurrentEnv;
HKEY hKeyMonitors;
HKEY hKeyCurrentMon;
SPLASSERT(pIniSpooler->SpoolerFlags & SPL_PRINT && pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER);
if (pszName && pszDLLName && pszEnvName && pszEnvDir)
{
//
// Check if we added an entry for this lang monitor already
// The cluster database for the spooler resource looks like:
//
// Parameters
// |
// +- Environments
// | |
// | +- Windows NT x86
// |
// +- OtherMonitors
// |
// +- Foo
// | |
// | +- Driver = Foo.dll
// |
// +- Bar
// |
// +- Driver = Bar.dll
//
if ((dwError = SplRegCreateKey(pIniSpooler->hckRoot,
ipszClusterDatabaseEnvironments,
0,
KEY_READ | KEY_WRITE,
NULL,
&hKeyEnvironments,
NULL,
pIniSpooler)) == ERROR_SUCCESS)
{
if ((dwError = SplRegCreateKey(hKeyEnvironments,
pszEnvName,
0,
KEY_READ | KEY_WRITE,
NULL,
&hKeyCurrentEnv,
NULL,
pIniSpooler)) == ERROR_SUCCESS)
{
if ((dwError = SplRegCreateKey(hKeyCurrentEnv,
szClusterNonAwareMonitors,
0,
KEY_READ | KEY_WRITE,
NULL,
&hKeyMonitors,
NULL,
pIniSpooler)) == ERROR_SUCCESS)
{
if ((dwError = SplRegCreateKey(hKeyMonitors,
pszName,
0,
KEY_READ | KEY_WRITE,
NULL,
&hKeyCurrentMon,
NULL,
pIniSpooler)) == ERROR_SUCCESS)
{
DWORD cbNeeded = 0;
//
// Check if this driver already exists in the database
//
if ((dwError=SplRegQueryValue(hKeyCurrentMon,
L"Driver",
NULL,
NULL,
&cbNeeded,
pIniSpooler))==ERROR_MORE_DATA)
{
DBGMSG(DBG_CLUSTER, ("CopyMonitorToClusterDisks "TSTR" already exists in cluster DB\n", pszName));
}
else
{
if (RegSetString(hKeyCurrentMon,
(LPWSTR)L"Driver",
(LPWSTR)pszDLLName,
&dwError,
pIniSpooler))
{
//
// Copy monitor file to cluster disk
//
WCHAR szMonitor[MAX_PATH];
WCHAR szDestDir[MAX_PATH];
if (GetSystemDirectory(szMonitor, COUNTOF(szMonitor)))
{
if ((dwError = StrNCatBuff(szMonitor,
COUNTOF(szMonitor),
szMonitor,
L"\\",
pszDLLName,
NULL)) == ERROR_SUCCESS &&
(dwError = StrNCatBuff(szDestDir,
COUNTOF(szDestDir),
pIniSpooler->pszClusResDriveLetter,
L"\\",
szClusterDriverRoot,
NULL)) == ERROR_SUCCESS)
{
dwError = CopyFileToDirectory(szMonitor, szDestDir, pszEnvDir, NULL, NULL);
}
}
else
{
dwError = GetLastError();
}
}
//
// If anything failed, delete the entry from the database
//
if (dwError != ERROR_SUCCESS)
{
dwError = SplRegDeleteKey(hKeyMonitors, pszName, pIniSpooler);
DBGMSG(DBG_CLUSTER, ("CopyMonitorToClusterDisks Error %u cleaned up cluster DB\n", dwError));
}
}
SplRegCloseKey(hKeyCurrentMon, pIniSpooler);
}
SplRegCloseKey(hKeyMonitors, pIniSpooler);
}
SplRegCloseKey(hKeyCurrentEnv, pIniSpooler);
}
SplRegCloseKey(hKeyEnvironments, pIniSpooler);
}
}
DBGMSG(DBG_CLUSTER, ("PropagateMonitorToCluster returns Win32 error %u\n", dwError));
return dwError;
}
/*++
Routine Name
InstallMonitorFromCluster
Routine Description:
For a cluster we keep the following printing resources in the
cluster data base: drivers, printers, ports, procs. We can also
have cluster aware port monitors. When the spooler initializes
those objects, it reads the data from the cluster data base.
When we write those objects we pass a handle to a key to some
spooler functions. (Ex WriteDriverIni) The handle is either to the
local registry or to the cluster database. This procedure is not safe
with language monitors. LMs keep data in the registry so you need
to supply a handle to a key in the reg. They don't work with clusters.
This function will do this:
1) read an association key in the cluster data base. We will have
information like (lm name, dll name) (see below where we store this)
2) copy the lm dll from the cluster disk to the local disk.
3) install the monitor with the local spooler
Theoretically this function works for both language and port monitors.
But it is useless when applied to port monitors.
Arguments:
pszName - monitor name
pszEnvName - envionment string Ex "Windows NT x86"
pszEnvDir - the path on disk for the environement Ex w32x86
pIniSpooler - cluster spooler
Return Value:
WIN32 error code
--*/
DWORD
InstallMonitorFromCluster(
IN LPCWSTR pszName,
IN LPCWSTR pszEnvName,
IN LPCWSTR pszEnvDir,
IN PINISPOOLER pIniSpooler
)
{
DWORD dwError = ERROR_INVALID_PARAMETER;
HKEY hKeyParent;
HKEY hKeyMon;
SPLASSERT(pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER);
if (pszName && pszEnvName && pszEnvDir)
{
HKEY hKeyEnvironments;
HKEY hKeyCurrentEnv;
HKEY hKeyMonitors;
HKEY hKeyCurrentMon;
//
// Check if we added an entry for this lang monitor already
// The cluster database for the spooler resource looks like:
//
// Parameters
// |
// +- Environments
// | |
// | +- Windows NT x86
// |
// +- OtherMonitors
// |
// +- Foo
// | |
// | +- Driver = Foo.dll
// |
// +- Bar
// |
// +- Driver = Bar.dll
//
if ((dwError = SplRegOpenKey(pIniSpooler->hckRoot,
ipszClusterDatabaseEnvironments,
KEY_READ,
&hKeyEnvironments,
pIniSpooler)) == ERROR_SUCCESS)
{
if ((dwError = SplRegOpenKey(hKeyEnvironments,
pszEnvName,
KEY_READ,
&hKeyCurrentEnv,
pIniSpooler)) == ERROR_SUCCESS)
{
if ((dwError = SplRegOpenKey(hKeyCurrentEnv,
szClusterNonAwareMonitors,
KEY_READ,
&hKeyMonitors,
pIniSpooler)) == ERROR_SUCCESS)
{
if ((dwError = SplRegOpenKey(hKeyMonitors,
pszName,
KEY_READ,
&hKeyCurrentMon,
pIniSpooler)) == ERROR_SUCCESS)
{
LPWSTR pszDLLName = NULL;
DWORD cchDLLName = 0;
if (RegGetString(hKeyCurrentMon,
(LPWSTR)L"Driver",
&pszDLLName,
&cchDLLName,
&dwError,
TRUE,
pIniSpooler))
{
//
// We found the monitor entry in the cluster DB
//
WCHAR szSource[MAX_PATH];
WCHAR szDest[MAX_PATH];
if (GetSystemDirectory(szDest, COUNTOF(szDest)))
{
if ((dwError = StrNCatBuff(szDest,
COUNTOF(szDest),
szDest,
L"\\",
pszDLLName,
NULL)) == ERROR_SUCCESS &&
(dwError = StrNCatBuff(szSource,
COUNTOF(szSource),
pIniSpooler->pszClusResDriveLetter,
L"\\",
szClusterDriverRoot,
L"\\",
pszEnvDir,
L"\\",
pszDLLName,
NULL)) == ERROR_SUCCESS)
{
//
// Copy file from K:\PrinterDrivers\W32x86\foo.dll to
// WINDIR\system32\foo.dll
//
if (CopyFile(szSource, szDest, FALSE))
{
MONITOR_INFO_2 Monitor;
Monitor.pDLLName = pszDLLName;
Monitor.pName = (LPWSTR)pszName;
Monitor.pEnvironment = (LPWSTR)pszEnvName;
DBGMSG(DBG_CLUSTER, ("InstallMonitorFromCluster "TSTR" copied\n", pszDLLName));
//
// Call AddMonitor to the local spooler
//
if (!SplAddMonitor(NULL, 2, (LPBYTE)&Monitor, pLocalIniSpooler))
{
dwError = GetLastError();
}
}
else
{
dwError = GetLastError();
}
}
}
else
{
dwError = GetLastError();
}
}
SplRegCloseKey(hKeyCurrentMon, pIniSpooler);
}
SplRegCloseKey(hKeyMonitors, pIniSpooler);
}
SplRegCloseKey(hKeyCurrentEnv, pIniSpooler);
}
SplRegCloseKey(hKeyEnvironments, pIniSpooler);
}
}
DBGMSG(DBG_CLUSTER, ("InstallMonitorFromCluster returns Win32 error %u\n", dwError));
return dwError;
}
/*++
Routine Name
CopyNewerOrOlderFiles
Routine Description:
Copies all newer or older files from the source directory to the dest dir.
If you supply a bool function that takes 2 parameters, it will apply that
function for each copied file. func(NULL, file.) This is useful when I have
to caopy over ICM profiles. Then I can resuse this function and have it
install those profiles as it copies them.
Arguments:
pszSourceDir - source directory string
pszDestDir - destination directory string
pfn - optional functin to be applied on each copied file
Return Value:
WIN32 error code
--*/
DWORD
CopyNewerOrOlderFiles(
IN LPCWSTR pszSourceDir,
IN LPCWSTR pszDestDir,
IN BOOL (WINAPI *pfn)(LPWSTR, LPWSTR)
)
{
DWORD dwError = ERROR_INVALID_PARAMETER;
if (pszSourceDir && pszDestDir)
{
WCHAR szSearchPath[MAX_PATH];
//
// Build the search path. We look for all files
//
if ((dwError = StrNCatBuff(szSearchPath,
COUNTOF(szSearchPath),
pszSourceDir,
L"\\*",
NULL)) == ERROR_SUCCESS)
{
WIN32_FIND_DATA SourceFindData;
HANDLE hSourceFind;
//
// Find first file that meets the criteria
//
if ((hSourceFind = FindFirstFile(szSearchPath, &SourceFindData)) != INVALID_HANDLE_VALUE)
{
do
{
WCHAR szMasterPath[MAX_PATH];
//
// Search for the rest of the files. We are interested in files that are not directories
//
if (!(SourceFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
(dwError = StrNCatBuff(szMasterPath,
COUNTOF(szMasterPath),
pszDestDir,
L"\\",
SourceFindData.cFileName,
NULL)) == ERROR_SUCCESS)
{
WIN32_FIND_DATA MasterFindData;
HANDLE hMasterFind;
BOOL bCopyFile = TRUE;
WCHAR szFile[MAX_PATH];
//
// Check if the file found in source dir exists in the dest dir
//
if ((hMasterFind = FindFirstFile(szMasterPath, &MasterFindData)) != INVALID_HANDLE_VALUE)
{
//
// Do not copy file if source and dest have same time stamp
//
if (!CompareFileTime(&SourceFindData.ftLastWriteTime, &MasterFindData.ftLastWriteTime))
{
bCopyFile = FALSE;
}
FindClose(hMasterFind);
}
//
// File either not found in dest dir, or it has a different timp stamp
//
if (bCopyFile &&
(dwError = StrNCatBuff(szFile,
COUNTOF(szFile),
pszSourceDir,
L"\\",
SourceFindData.cFileName,
NULL)) == ERROR_SUCCESS &&
(dwError = CopyFile(szFile,
szMasterPath,
FALSE) ? ERROR_SUCCESS : GetLastError()) == ERROR_SUCCESS &&
pfn)
{
dwError = (*pfn)(NULL, szFile) ? ERROR_SUCCESS : GetLastError();
}
}
} while (dwError == ERROR_SUCCESS && FindNextFile(hSourceFind, &SourceFindData));
FindClose(hSourceFind);
}
else if ((dwError = GetLastError()) == ERROR_PATH_NOT_FOUND || dwError == ERROR_FILE_NOT_FOUND)
{
//
// No directory or files, success
//
dwError = ERROR_SUCCESS;
}
}
}
DBGMSG(DBG_CLUSTER, ("CopyNewerOrOlderFiles returns Win32 error %u\n", dwError));
return dwError;
}
/*++
Routine Name
CopyICMFromClusterDiskToLocalDisk
Routine Description:
Copies all newer or older files from the source directory to the destination
directory. The source directory is the ICM directory on the cluster disk
and the destination is the ICM directory on the local machine for the
cluster spooler. This function will also install the icm profiles with the
local machine/
Arguments:
pIniSpooler - pointer to cluster spooler structure
Return Value:
WIN32 error code
--*/
DWORD
CopyICMFromClusterDiskToLocalDisk(
IN PINISPOOLER pIniSpooler
)
{
DWORD dwError = ERROR_SUCCESS;
WCHAR szSource[MAX_PATH];
WCHAR szDest[MAX_PATH];
SPLASSERT(pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER);
if ((dwError = StrNCatBuff(szDest,
COUNTOF(szDest),
pIniSpooler->pDir,
L"\\Drivers\\Color",
NULL)) == ERROR_SUCCESS &&
(dwError = StrNCatBuff(szSource,
COUNTOF(szSource),
pIniSpooler->pszClusResDriveLetter,
L"\\",
szClusterDriverRoot,
L"\\",
L"Color",
NULL)) == ERROR_SUCCESS)
{
HMODULE hLib;
typedef BOOL (WINAPI *PFN)(LPWSTR, LPWSTR);
PFN pfn;
//
// Make sure the directory on the local disk exists. We will copy files
// from K:\Printerdrivers\Color to
// WINDIR\system32\spool\drivers\clus-spl-guid\drivers\color
//
CreateCompleteDirectory(szDest);
if ((hLib = LoadLibrary(L"mscms.dll")) &&
(pfn = (PFN)GetProcAddress(hLib, "InstallColorProfileW")))
{
dwError = CopyNewerOrOlderFiles(szSource, szDest, pfn);
}
else
{
dwError = GetLastError();
}
if (hLib)
{
FreeLibrary(hLib);
}
DBGMSG(DBG_CLUSTER, ("CopyICMFromClusterDiskToLocalDisk "TSTR" "TSTR" Error %u\n", szSource, szDest, dwError));
}
return dwError;
}
/*++
Routine Name
CopyICMFromLocalDiskToClusterDisk
Routine Description:
Copies all newer or older files from the source directory to the destination
directory. The source directory is the ICM directory on the local machine for
the cluster spooler. The destination is the ICM directory on the cluster disk
Arguments:
pIniSpooler - pointer to cluster spooler structure
Return Value:
WIN32 error code
--*/
DWORD
CopyICMFromLocalDiskToClusterDisk(
IN PINISPOOLER pIniSpooler
)
{
DWORD dwError = ERROR_SUCCESS;
WCHAR szSource[MAX_PATH];
WCHAR szDest[MAX_PATH];
SPLASSERT(pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER);
if ((dwError = StrNCatBuff(szSource,
COUNTOF(szSource),
pIniSpooler->pDir,
L"\\Drivers\\Color",
NULL)) == ERROR_SUCCESS &&
(dwError = StrNCatBuff(szDest,
COUNTOF(szDest),
pIniSpooler->pszClusResDriveLetter,
L"\\",
szClusterDriverRoot,
L"\\",
L"Color",
NULL)) == ERROR_SUCCESS &&
//
// Make sure the destination on the cluster disk exists. We need to create
// it with special access rights. (only admins and system can read/write)
//
((dwError = CreateProtectedDirectory(szDest)) == ERROR_SUCCESS ||
dwError == ERROR_ALREADY_EXISTS))
{
dwError = CopyNewerOrOlderFiles(szSource, szDest, NULL);
DBGMSG(DBG_CLUSTER, ("CopyICMFromLocalDiskToClusterDisk "TSTR" "TSTR" Error %u\n", szSource, szDest, dwError));
}
return dwError;
}
/*++
Routine Name
CreateClusterSpoolerEnvironmentsStructure
Routine Description:
A pIniSpooler needs a list of all possible pIniEnvironemnts. For the local
spooler the setup creates the necessary environments keys in the registry under
HKLM\System\CCS\Control\Print\Environments. We need to propagate the same
structure for each cluster spooler in the cluster data base.
Relies on the fact that pLocalIniSpooler is initialized fisrt (among all
pinispoolers)
This function builds the following strucutre in the cluster database
Parameters
|
+- Environments
| |
| +- Windows NT x86
| | (Directory = w32x86)
| |
| +- Windows 4.0
| | (Directory = win40)
| |
Arguments:
pIniSpooler - pointer to cluster spooler structure
Return Value:
WIN32 error code
--*/
DWORD
CreateClusterSpoolerEnvironmentsStructure(
IN PINISPOOLER pIniSpooler
)
{
HKEY hEnvironmentsKey;
DWORD dwError;
SPLASSERT(pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER);
if ((dwError = SplRegCreateKey(pIniSpooler->hckRoot,
pIniSpooler->pszRegistryEnvironments,
0,
KEY_ALL_ACCESS,
NULL,
&hEnvironmentsKey,
NULL,
pIniSpooler)) == ERROR_SUCCESS)
{
PINIENVIRONMENT pIniEnvironment;
//
// Loop through the environments of the local spooler
//
for (pIniEnvironment = pLocalIniSpooler->pIniEnvironment;
pIniEnvironment && dwError == ERROR_SUCCESS;
pIniEnvironment = pIniEnvironment->pNext)
{
HKEY hKeyCurrentEnv;
if ((dwError = SplRegCreateKey(hEnvironmentsKey,
pIniEnvironment->pName,
0,
KEY_ALL_ACCESS,
NULL,
&hKeyCurrentEnv,
NULL,
pIniSpooler)) == ERROR_SUCCESS)
{
HKEY hKeyPrtProc;
//
// Set the value Directory (ex. = W32X86) and create
// the print processor key
//
if (RegSetString(hKeyCurrentEnv,
szDirectory,
pIniEnvironment->pDirectory,
&dwError,
pIniSpooler) &&
(dwError = SplRegCreateKey(hKeyCurrentEnv,
szPrintProcKey,
0,
KEY_ALL_ACCESS,
NULL,
&hKeyPrtProc,
NULL,
pIniSpooler)) == ERROR_SUCCESS)
{
SplRegCloseKey(hKeyPrtProc, pIniSpooler);
}
SplRegCloseKey(hKeyCurrentEnv, pIniSpooler);
}
}
SplRegCloseKey(hEnvironmentsKey, pIniSpooler);
}
DBGMSG(DBG_CLUSTER, ("CreateClusterSpoolerEnvironmentsStructure returns dwError %u\n\n", dwError));
return dwError;
}
/*++
Routine Name
AddLocalDriverToClusterSpooler
Routine Description:
Adds a driver that exists on the local inispooler to the cluster spooler.
It will also add all versions of that driver. We use this function in the
upgrade scenario. For a certain printer, we need to propage the driver
(and all the versions of that driver) to the cluster data base and cluster
disk.
Arguments:
pIniSpooler - pointer to cluster spooler structure
Return Value:
WIN32 error code
--*/
DWORD
AddLocalDriverToClusterSpooler(
IN LPCWSTR pszDriver,
IN PINISPOOLER pIniSpooler
)
{
DWORD dwError = ERROR_SUCCESS;
PINIENVIRONMENT pIniEnv;
PINIVERSION pIniVer;
SplInSem();
//
// Traverse all environments and versions
//
for (pIniEnv=pLocalIniSpooler->pIniEnvironment; pIniEnv; pIniEnv=pIniEnv->pNext)
{
for (pIniVer=pIniEnv->pIniVersion; pIniVer; pIniVer=pIniVer->pNext)
{
PINIDRIVER pIniDriver = (PINIDRIVER)FindIniKey((PINIENTRY)pIniVer->pIniDriver, (LPWSTR)pszDriver);
if (pIniDriver)
{
DRIVER_INFO_6 DriverInfo = {0};
WCHAR szDriverFile[MAX_PATH];
WCHAR szDataFile[MAX_PATH];
WCHAR szConfigFile[MAX_PATH];
WCHAR szHelpFile[MAX_PATH];
WCHAR szPrefix[MAX_PATH];
LPWSTR pszzDependentFiles = NULL;
//
// Get fully qualified driver file paths. We will call add printer
// driver without using the scratch directory
//
if ((dwError = StrNCatBuff(szDriverFile,
COUNTOF(szDriverFile),
pLocalIniSpooler->pDir,
szDriversDirectory, L"\\",
pIniEnv->pDirectory, L"\\",
pIniVer->szDirectory, L"\\",
pIniDriver->pDriverFile,
NULL)) == ERROR_SUCCESS &&
(dwError = StrNCatBuff(szDataFile,
COUNTOF(szDataFile),
pLocalIniSpooler->pDir,
szDriversDirectory, L"\\",
pIniEnv->pDirectory, L"\\",
pIniVer->szDirectory, L"\\",
pIniDriver->pDataFile,
NULL)) == ERROR_SUCCESS &&
(dwError = StrNCatBuff(szConfigFile,
COUNTOF(szConfigFile),
pLocalIniSpooler->pDir,
szDriversDirectory, L"\\",
pIniEnv->pDirectory, L"\\",
pIniVer->szDirectory, L"\\",
pIniDriver->pConfigFile,
NULL)) == ERROR_SUCCESS &&
(dwError = StrNCatBuff(szHelpFile,
COUNTOF(szHelpFile),
pLocalIniSpooler->pDir,
szDriversDirectory, L"\\",
pIniEnv->pDirectory, L"\\",
pIniVer->szDirectory, L"\\",
pIniDriver->pHelpFile,
NULL)) == ERROR_SUCCESS &&
(dwError = StrNCatBuff(szPrefix,
COUNTOF(szPrefix),
pLocalIniSpooler->pDir,
szDriversDirectory, L"\\",
pIniEnv->pDirectory, L"\\",
pIniVer->szDirectory, L"\\",
NULL)) == ERROR_SUCCESS &&
(dwError = StrCatPrefixMsz(szPrefix,
pIniDriver->pDependentFiles,
&pszzDependentFiles)) == ERROR_SUCCESS)
{
DBGMSG(DBG_CLUSTER, ("AddLocalDrv szDriverFile "TSTR"\n", szDriverFile));
DBGMSG(DBG_CLUSTER, ("AddLocalDrv szDataFile "TSTR"\n", szDataFile));
DBGMSG(DBG_CLUSTER, ("AddLocalDrv szConfigFile "TSTR"\n", szConfigFile));
DBGMSG(DBG_CLUSTER, ("AddLocalDrv szHelpFile "TSTR"\n", szHelpFile));
DBGMSG(DBG_CLUSTER, ("AddLocalDrv szPrefix "TSTR"\n", szPrefix));
DriverInfo.pDriverPath = szDriverFile;
DriverInfo.pName = pIniDriver->pName;
DriverInfo.pEnvironment = pIniEnv->pName;
DriverInfo.pDataFile = szDataFile;
DriverInfo.pConfigFile = szConfigFile;
DriverInfo.cVersion = pIniDriver->cVersion;
DriverInfo.pHelpFile = szHelpFile;
DriverInfo.pMonitorName = pIniDriver->pMonitorName;
DriverInfo.pDefaultDataType = pIniDriver->pDefaultDataType;
DriverInfo.pDependentFiles = pszzDependentFiles;
DriverInfo.pszzPreviousNames = pIniDriver->pszzPreviousNames;
DriverInfo.pszMfgName = pIniDriver->pszMfgName;
DriverInfo.pszOEMUrl = pIniDriver->pszOEMUrl;
DriverInfo.pszHardwareID = pIniDriver->pszHardwareID;
DriverInfo.pszProvider = pIniDriver->pszProvider;
DriverInfo.dwlDriverVersion = pIniDriver->dwlDriverVersion;
DriverInfo.ftDriverDate = pIniDriver->ftDriverDate;
LeaveSplSem();
if (!SplAddPrinterDriverEx(NULL,
6,
(LPBYTE)&DriverInfo,
APD_COPY_NEW_FILES,
pIniSpooler,
FALSE,
DO_NOT_IMPERSONATE_USER))
{
dwError = GetLastError();
}
EnterSplSem();
}
FreeSplMem(pszzDependentFiles);
DBGMSG(DBG_CLUSTER, ("AddLocalDrv Env "TSTR" Ver "TSTR" Name "TSTR" Error %u\n",
pIniEnv->pName, pIniVer->pName, pszDriver, dwError));
}
}
}
return dwError;
}
/*++
Routine Name
StrCatPerfixMsz
Routine Description:
Take a prefix which is a string Ex "C:\windows\" and a multi sz Ex: "a0b00"
It will create: c:\windows\a0c:\windows\b00
The prefix must have a trailing "\".
Arguments:
pszPrefix - string to prefix all the strings in the msz
pszzFiles - msz of files
ppszzFullPathFiles - out param
Return Value:
WIN32 error code
--*/
DWORD
StrCatPrefixMsz(
IN LPCWSTR pszPrefix,
IN LPWSTR pszzFiles,
OUT LPWSTR *ppszzFullPathFiles
)
{
DWORD dwError = ERROR_INVALID_PARAMETER;
if (pszPrefix && ppszzFullPathFiles)
{
*ppszzFullPathFiles = NULL;
if (pszzFiles)
{
WCHAR szNewPath[MAX_PATH] = {0};
LPWSTR psz;
LPWSTR pszReturn;
LPWSTR pszTemp;
DWORD cbNeeded = 0;
DWORD dwPrifxLen = wcslen(pszPrefix);
//
// We count the number of character of the string
// that we need to allocate
//
for (psz = pszzFiles; psz && *psz;)
{
DWORD dwLen = wcslen(psz);
cbNeeded += dwPrifxLen + dwLen + 1;
psz += dwLen + 1;
}
//
// Final \0 of the multi sz
//
cbNeeded++;
//
// Convert to number of bytes
//
cbNeeded *= sizeof(WCHAR);
if (pszReturn = AllocSplMem(cbNeeded))
{
for (psz = pszzFiles, pszTemp = pszReturn; psz && *psz; )
{
wcscpy(pszTemp, pszPrefix);
wcscat(pszTemp, psz);
pszTemp += wcslen(pszTemp);
*pszTemp = L'\0';
pszTemp++;
psz += wcslen(psz) + 1;
}
pszTemp = L'\0';
//
// Set out param
//
*ppszzFullPathFiles = pszReturn;
dwError = ERROR_SUCCESS;
}
else
{
dwError = GetLastError();
}
}
else
{
//
// NULL input multi sz, nothing to do
//
dwError = ERROR_SUCCESS;
}
}
DBGMSG(DBG_CLUSTER, ("StrCatPerfixMsz returns %u\n", dwError));
return dwError;
}
/*++
Routine Name
ClusterSplReadUpgradeKey
Routine Description:
After the first reboot following an upgrade of a node, the cluster
service informs the resdll that a version change occured. At this
time out spooler resource may be running on another node or may
not be actie at all. Thus the resdll writes a value in the local
registry. The vaue name is the GUID for the spooler resource, the
value is DWORD 1. When the cluster spooler resource fails over on this
machine it (i.e. now) it queries for that value to know if it needs
to preform post upgrade operations, like upgrading the printer drivers.
Arguments:
pszResource - string respresenation of GUID for the cluster resource
pdwVlaue - will contain the value in the registry for the GUID
Return Value:
WIN32 error code
--*/
DWORD
ClusterSplReadUpgradeKey(
IN LPCWSTR pszResourceID,
OUT LPDWORD pdwValue
)
{
DWORD dwError = ERROR_INVALID_PARAMETER;
HKEY hkRoot = NULL;
HKEY hkUpgrade = NULL;
if (pszResourceID && pdwValue)
{
*pdwValue = 0;
if ((dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
SPLREG_CLUSTER_LOCAL_ROOT_KEY,
0,
KEY_READ,
&hkRoot)) == ERROR_SUCCESS &&
(dwError = RegOpenKeyEx(hkRoot,
SPLREG_CLUSTER_UPGRADE_KEY,
0,
KEY_READ,
&hkUpgrade)) == ERROR_SUCCESS)
{
DWORD cbNeeded = sizeof(DWORD);
dwError = RegQueryValueEx(hkUpgrade, pszResourceID, NULL, NULL, (LPBYTE)pdwValue, &cbNeeded);
}
if (hkUpgrade) RegCloseKey(hkUpgrade);
if (hkRoot) RegCloseKey(hkRoot);
//
// Regardless of what happened, return success
//
dwError = ERROR_SUCCESS;
}
return dwError;
}
/*++
Routine Name
ClusterSplReadUpgradeKey
Routine Description:
After the first reboot following an upgrade of a node, the cluster
service informs the resdll that a version change occured. At this
time out spooler resource may be running on another node or may
not be actie at all. Thus the resdll writes a value in the local
registry. The vaue name is the GUID for the spooler resource, the
value is DWORD 1. When the cluster spooler resource fails over on this
machine it (i.e. now) it queries for that value to know if it needs
to preform post upgrade operations, like upgrading the printer drivers.
After the spooler preforms upgrade taks, it will delete the value
corresponding to its GUID. Also if that value is the only one under the
SPLREG_CLUSTER_UPGRADE_KEY key, it will delete that key.
Arguments:
pszResource - string respresenation of GUID for the cluster resource
Return Value:
WIN32 error code
--*/
DWORD
ClusterSplDeleteUpgradeKey(
IN LPCWSTR pszResourceID
)
{
DWORD dwError = ERROR_INVALID_PARAMETER;
HKEY hkRoot = NULL;
HKEY hkUpgrade = NULL;
if ((dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
SPLREG_CLUSTER_LOCAL_ROOT_KEY,
0,
KEY_READ,
&hkRoot)) == ERROR_SUCCESS &&
(dwError = RegOpenKeyEx(hkRoot,
SPLREG_CLUSTER_UPGRADE_KEY,
0,
KEY_ALL_ACCESS,
&hkUpgrade)) == ERROR_SUCCESS)
{
DWORD cValues = 0;
dwError = RegDeleteValue(hkUpgrade, pszResourceID);
if (RegQueryInfoKey(hkUpgrade,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&cValues,
NULL,
NULL,
NULL,
NULL) == ERROR_SUCCESS && !cValues)
{
RegDeleteKey(hkRoot, SPLREG_CLUSTER_UPGRADE_KEY);
}
if (hkUpgrade) RegCloseKey(hkUpgrade);
if (hkRoot) RegCloseKey(hkRoot);
}
return dwError;
}
/*++
Routine Name
RunProcess
Routine Description:
Creates a process. Waits for it to terminate.
Arguments:
pszExe - program to execute (muist be fully qualfied)
pszCommand - command line to execute
dwTimeOut - time to wait for the process to terminate
pszExitCode - pointer to reveice exit code of process
Return Value:
WIN32 error code
--*/
DWORD
RunProcess(
IN LPCWSTR pszExe,
IN LPCWSTR pszCommand,
IN DWORD dwTimeOut,
OUT LPDWORD pdwExitCode OPTIONAL
)
{
DWORD dwError = ERROR_INVALID_PARAMETER;
if (pszCommand && pszExe)
{
PROCESS_INFORMATION ProcInfo = {0};
STARTUPINFO StartInfo = {0};
StartInfo.cb = sizeof(StartInfo);
StartInfo.dwFlags = 0;
if (!CreateProcess(pszExe,
(LPWSTR)pszCommand,
NULL,
NULL,
FALSE,
CREATE_NO_WINDOW,
NULL,
NULL,
&StartInfo,
&ProcInfo))
{
dwError = GetLastError();
}
else
{
if (WaitForSingleObject(ProcInfo.hProcess, dwTimeOut) == WAIT_OBJECT_0)
{
//
// Process executed fine
//
dwError = ERROR_SUCCESS;
}
if (pdwExitCode && !GetExitCodeProcess(ProcInfo.hProcess, pdwExitCode))
{
*pdwExitCode = 0;
}
CloseHandle(ProcInfo.hThread);
CloseHandle(ProcInfo.hProcess);
}
}
return dwError;
}
/*++
Routine Name
GetLocalArchEnv
Routine Description:
Helper function. Returns a pointer to the environment
that matches the architecture of the local machine.
The environemnt is taken off the pInSpooler passed as
argument
Arguments:
pIniSpooler - pointer to spooler structure
Return Value:
PINIENVIRONMENT
--*/
PINIENVIRONMENT
GetLocalArchEnv(
IN PINISPOOLER pIniSpooler
)
{
SplInSem();
//
// The local spooler and cluster spooler do not share the same Environment structures.
//
return pIniSpooler && pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ?
FindEnvironment(szEnvironment, pIniSpooler) : pThisEnvironment;
}
/*++
Routine Name
ClusterFindLanguageMonitor
Routine Description:
If a valid monitor name is specified and the monitor
is not found in the specified pIniSpooler, then the
function will try to install the monitor from the
cluster disk.
Arguments:
pszMonitor - monitor name
pszEnvName - name of environment of the lm
pIniSpooler - pointer to cluster spooler structure
Return Value:
Win32 error code
--*/
DWORD
ClusterFindLanguageMonitor(
IN LPCWSTR pszMonitor,
IN LPCWSTR pszEnvName,
IN PINISPOOLER pIniSpooler
)
{
DWORD dwError = ERROR_SUCCESS;
//
// This is the moment where we check if we need to add the monitor
//
if (pszMonitor && *pszMonitor)
{
PINIMONITOR pIniLangMonitor;
EnterSplSem();
//
// We need to find the language monitor in pLocalIniSpooler
// LMs are not cluster aware, so the cluster pIniSpooler doesn't know about them
//
pIniLangMonitor = FindMonitor(pszMonitor, pLocalIniSpooler);
LeaveSplSem();
if (!pIniLangMonitor)
{
PINIENVIRONMENT pIniEnvironment;
EnterSplSem();
pIniEnvironment = FindEnvironment(pszEnvName, pIniSpooler);
LeaveSplSem();
if (pIniEnvironment)
{
DBGMSG(DBG_CLUSTER, ("ClusterFindLanguageMonitor Trying to install LangMonitor "TSTR"\n", pszMonitor));
//
// We try to install the monitor from the cluster disk to the local spooler
//
dwError = InstallMonitorFromCluster(pszMonitor,
pIniEnvironment->pName,
pIniEnvironment->pDirectory,
pIniSpooler);
}
else
{
dwError = ERROR_INVALID_ENVIRONMENT;
}
}
}
DBGMSG(DBG_CLUSTER, ("ClusterFindLanguageMonitor LangMonitor "TSTR" return Win32 error %u\n", pszMonitor, dwError));
return dwError;
}
/*++
Routine Name
WriteTimeStamp
Routine Description:
Opens the key hkRoot\pszSubkey1\...\pszSubKey5
and writes the value of szClusDrvTimeStamp (binary
data representing a system time)
Arguments:
hkRoot - handle to driver key
SysTime - system time structure
pszSubKey1 - subkey of the root key
pszSubKey2 - subkey of key1, can be null
pszSubKey3 - subkey of key2, can be null
pszSubKey4 - subkey of key3, can be null
pszSubKey5 - subkey of key4, can be null
pIniSpooler - spooler, can be NULL
Return Value:
WIN32 error code
--*/
DWORD
WriteTimeStamp(
IN HKEY hkRoot,
IN SYSTEMTIME SysTime,
IN LPCWSTR pszSubKey1,
IN LPCWSTR pszSubKey2,
IN LPCWSTR pszSubKey3,
IN LPCWSTR pszSubKey4,
IN LPCWSTR pszSubKey5,
IN PINISPOOLER pIniSpooler
)
{
DWORD dwError = ERROR_INVALID_PARAMETER;
if (hkRoot)
{
LPCWSTR ppszKeyNames[] = {NULL, pszSubKey1, pszSubKey2, pszSubKey3, pszSubKey4, pszSubKey5};
HKEY pKeyHandles[] = {hkRoot, NULL, NULL, NULL, NULL, NULL};
DWORD uIndex;
dwError = ERROR_SUCCESS;
//
// Open all the keys
//
for (uIndex = 1;
uIndex < COUNTOF(ppszKeyNames) &&
dwError == ERROR_SUCCESS &&
ppszKeyNames[uIndex];
uIndex++)
{
DBGMSG(DBG_CLUSTER, ("KEY "TSTR"\n", ppszKeyNames[uIndex]));
dwError = SplRegCreateKey(pKeyHandles[uIndex-1],
ppszKeyNames[uIndex],
0,
KEY_ALL_ACCESS,
NULL,
&pKeyHandles[uIndex],
NULL,
pIniSpooler);
}
//
// If we opened successfully the keys that we wanted, write the value
//
if (dwError == ERROR_SUCCESS &&
!RegSetBinaryData(pKeyHandles[uIndex-1],
szClusDrvTimeStamp,
(LPBYTE)&SysTime,
sizeof(SysTime),
&dwError,
pIniSpooler))
{
dwError = GetLastError();
}
//
// Close any keys that we opened
//
for (uIndex = 1; uIndex < COUNTOF(ppszKeyNames); uIndex++)
{
if (pKeyHandles[uIndex])
{
SplRegCloseKey(pKeyHandles[uIndex], pIniSpooler);
}
}
}
DBGMSG(DBG_CLUSTER, ("WriteTimeStamp returns Win32 error %u\n", dwError));
return dwError;
}
/*++
Routine Name
ReadTimeStamp
Routine Description:
Opens the key hkRoot\pszSubkey1\...\pszSubKey5
and reads the value of szClusDrvTimeStamp (binary
data representing a system time)
Arguments:
hkRoot - handle to driver key
pSysTime - pointer to allocated system time structure
pszSubKey1 - subkey of the root key
pszSubKey2 - subkey of key1, can be null
pszSubKey3 - subkey of key2, can be null
pszSubKey4 - subkey of key3, can be null
pszSubKey5 - subkey of key4, can be null
pIniSpooler - spooler, can be NULL
Return Value:
WIN32 error code
--*/
DWORD
ReadTimeStamp(
IN HKEY hkRoot,
IN OUT SYSTEMTIME *pSysTime,
IN LPCWSTR pszSubKey1,
IN LPCWSTR pszSubKey2,
IN LPCWSTR pszSubKey3,
IN LPCWSTR pszSubKey4,
IN LPCWSTR pszSubKey5,
IN PINISPOOLER pIniSpooler
)
{
DWORD dwError = ERROR_INVALID_PARAMETER;
if (hkRoot && pSysTime)
{
LPCWSTR ppszKeyNames[] = {NULL, pszSubKey1, pszSubKey2, pszSubKey3, pszSubKey4, pszSubKey5};
HKEY pKeyHandles[] = {hkRoot, NULL, NULL, NULL, NULL, NULL};
DWORD uIndex;
dwError = ERROR_SUCCESS;
//
// Open all the keys
//
for (uIndex = 1;
uIndex < COUNTOF(ppszKeyNames) &&
dwError == ERROR_SUCCESS &&
ppszKeyNames[uIndex];
uIndex++)
{
dwError = SplRegCreateKey(pKeyHandles[uIndex-1],
ppszKeyNames[uIndex],
0,
KEY_ALL_ACCESS,
NULL,
&pKeyHandles[uIndex],
NULL,
pIniSpooler);
}
//
// If we opened successfully the keys that we wanted, write the value
//
if (dwError == ERROR_SUCCESS)
{
DWORD cbSize = sizeof(SYSTEMTIME);
dwError = SplRegQueryValue(pKeyHandles[uIndex-1],
szClusDrvTimeStamp,
NULL,
(LPBYTE)pSysTime,
&cbSize,
pIniSpooler);
}
//
// Close any keys that we opened
//
for (uIndex = 1; uIndex < COUNTOF(ppszKeyNames); uIndex++)
{
if (pKeyHandles[uIndex])
{
SplRegCloseKey(pKeyHandles[uIndex], pIniSpooler);
}
}
}
DBGMSG(DBG_CLUSTER, ("ReadTimeStamp returns Win32 error %u\n", dwError));
return dwError;
}
/*++
Routine Name
ClusterCheckDriverChanged
Routine Description:
Helper function for spooler start up. When we have a cluster spooler
and we build the environments and drivers, we need to check if the
drivers on the local machine (in print$\GUID) are in sync with the
drivers on the cluster disk. We store a time stamp in the cluster
data base. The time stamp indicate when the last update on the driver
occured. The same type of time stamp is stroed in the lcoal registry
(for our cluster spooler). If the 2 time stamp are different, then
we need to call an add printer driver and use data from the cluster
disk. The drivers on the cluster spooler were updated while it was
running on a different node.
Arguments:
hClusterVersionKey - handle to the driver version key
pszDriver - driver name
pszEnv - driver environment name
pszVer - driver version name
pIniSpooler - spooler
Return Value:
TRUE - if the driver on the cluster disk is updated and we need to
call add printer driver. If anything fails in this function,
then we also return TRUE, to force our caller to update/add
the driver in question.
FALSE - if the drivers on the local mahcine and the cluster spooler
are in sync
--*/
BOOL
ClusterCheckDriverChanged(
IN HKEY hClusterVersionKey,
IN LPCWSTR pszDriver,
IN LPCWSTR pszEnv,
IN LPCWSTR pszVer,
IN PINISPOOLER pIniSpooler
)
{
BOOL bReturn = TRUE;
if (hClusterVersionKey &&
pszDriver &&
pszEnv &&
pszVer)
{
SYSTEMTIME ClusterTime;
SYSTEMTIME NodeTime;
if (ReadTimeStamp(hClusterVersionKey,
&ClusterTime,
pszDriver,
NULL,
NULL,
NULL,
NULL,
pIniSpooler) == ERROR_SUCCESS &&
ReadTimeStamp(HKEY_LOCAL_MACHINE,
&NodeTime,
ipszRegistryClusRepository,
pIniSpooler->pszClusResID,
pszEnv,
pszVer,
pszDriver,
NULL) == ERROR_SUCCESS &&
!memcmp(&ClusterTime, &NodeTime, sizeof(SYSTEMTIME)))
{
bReturn = FALSE;
}
}
DBGMSG(DBG_CLUSTER, ("ClusterCheckDriverChanged returns bool %u\n", bReturn));
return bReturn;
}
/*++
Routine Name
IsLocalFile
Routine Description:
Checks if a file is on the local machine. If the file path is
"\\machinename\sharename\...\filename" then machinename is checked
against pIniSpooler->pMachineName and pIniSpooler->ppszOtherNames.
Arguments:
pszFileName - file name
pIniSpooler - INISPOOLER structure
Return Value:
TRUE if the file is placed locally.
FALSE if the file is placed remotely.
--*/
BOOL
IsLocalFile (
IN LPCWSTR pszFileName,
IN PINISPOOLER pIniSpooler
)
{
LPWSTR pEndOfMachineName, pMachineName;
BOOL bRetValue = TRUE;
if (pszFileName &&
*pszFileName == L'\\' &&
*(pszFileName+1) == L'\\')
{
//
// If first 2 charactes in pszFileName are '\\',
// then search for the next '\\'. If found, then set it to 0,
// to isolate the machine name.
//
pMachineName = (LPWSTR)pszFileName;
if (pEndOfMachineName = wcschr(pszFileName + 2, L'\\'))
{
*pEndOfMachineName = 0;
}
bRetValue = CheckMyName(pMachineName, pIniSpooler);
//
// Restore pszFileName.
//
if (pEndOfMachineName)
{
*pEndOfMachineName = L'\\';
}
}
return bRetValue;
}
/*++
Routine Name
IsEXEFile
Routine Description:
Checks if a file is a executable.
The check is made against file extension, which isn't quite
accurate.
Arguments:
pszFileName - file name
Return Value:
TRUE if the file extension is either .EXE or .DLL
--*/
BOOL
IsEXEFile(
IN LPCWSTR pszFileName
)
{
BOOL bRetValue = FALSE;
DWORD dwLength;
LPWSTR pszExtension;
if (pszFileName && *pszFileName)
{
dwLength = wcslen(pszFileName);
if (dwLength > COUNTOF(L".EXE") - 1)
{
pszExtension = (LPWSTR)pszFileName + dwLength - (COUNTOF(L".EXE") - 1);
if (_wcsicmp(pszExtension , L".EXE") == 0 ||
_wcsicmp(pszExtension , L".DLL") == 0)
{
bRetValue = TRUE;
}
}
}
return bRetValue;
}
/*++
Routine Name
PackStringToEOB
Routine Description:
Copies a string to the end of buffer.
The buffer must be big enough so that it can hold the string.
This function is called by Get/Enum APIs that build BLOB buffers to
send them with RPC.
Arguments:
pszSource - string to by copied to the end of buffer
pEnd - a pointer to the end of a pre-allocated buffer.
Return Value:
The pointer to the end of buffer after the sting was appended.
NULL if an error occured.
--*/
LPBYTE
PackStringToEOB(
IN LPWSTR pszSource,
IN LPBYTE pEnd
)
{
DWORD cbStr;
//
// Align the end of buffer to WORD boundaries.
//
WORD_ALIGN_DOWN(pEnd);
if (pszSource && pEnd)
{
cbStr = (wcslen(pszSource) + 1) * sizeof(WCHAR);
pEnd -= cbStr;
CopyMemory(pEnd, pszSource, cbStr);
}
else
{
pEnd = NULL;
}
return pEnd;
}
LPVOID
MakePTR (
IN LPVOID pBuf,
IN DWORD Quantity
)
/*++
Routine Name
MakePTR
Routine Description:
Makes a pointer by adding a quantity to the beginning of a buffer.
Arguments:
pBuf - pointer to buffer
DWORD - quantity
Return Value:
LPVOID pointer
--*/
{
return (LPVOID)((ULONG_PTR)pBuf + (ULONG_PTR)Quantity);
}
DWORD
MakeOffset (
IN LPVOID pFirst,
IN LPVOID pSecond
)
/*++
Routine Name
MakeOffset
Routine Description:
Substarcts two pointers.
Arguments:
pFirst - pointer to buffer
pSecond - pointer to buffer
Return Value:
DWORD
--*/
{
return (DWORD)((ULONG_PTR)pFirst - (ULONG_PTR)pSecond);
}
/*++
Routine Name
IsValidPrinterName
Routine Description:
Checks if a string is a valid printer name.
Arguments:
pszPrinter - pointer to string
cchMax - max number of chars to scan
Return Value:
TRUE - the string is a valid printer name
FALSE - the string is a invalid printer name. The function set the last error
to ERROR_INVALID_PRINTER_NAME in this case
--*/
BOOL
IsValidPrinterName(
IN LPCWSTR pszPrinter,
IN DWORD cchMax
)
{
DWORD Error = ERROR_INVALID_PRINTER_NAME;
//
// A printer name is of the form:
//
// \\s\p or p
//
// The name cannot contain the , character. Note that the add printer
// wizard doesn't accept "!" as a valid printer name. We wanted to do
// the same here, but we regressed in app compat with 9x apps.
// The number of \ in the name is 0 or 3
// If the name contains \, then the fist 2 chars must be \.
// The printer name cannot end in \.
// After leading "\\" then next char must not be \
// The minimum length is 1 character
// The maximum length is MAX_UNC_PRINTER_NAME
//
if (!IsBadStringPtr(pszPrinter, cchMax) && pszPrinter && *pszPrinter)
{
UINT uSlashCount = 0;
UINT uLen = 0;
LPCWSTR p;
Error = ERROR_SUCCESS;
//
// Count characters
//
for (p = pszPrinter; *p && uLen <= cchMax; p++, uLen++)
{
if (*p == L',')
{
Error = ERROR_INVALID_PRINTER_NAME;
break;
}
else if (*p == L'\\')
{
uSlashCount++;
}
}
//
// Perform validation
//
if (Error == ERROR_SUCCESS &&
//
// Validate length
//
(uLen > cchMax ||
//
// The printer name has either no \, or exactly 3 \.
//
uSlashCount && uSlashCount != 3 ||
//
// A printer name that contains 3 \, must have the first 2 chars \ and the 3 not \.
// The last char cannot be \.
// Ex "\Foo", "F\oo", "\\\Foo", "\\Foo\" are invalid.
// Ex. "\\srv\bar" is valid.
//
uSlashCount == 3 && (pszPrinter[0] != L'\\' ||
pszPrinter[1] != L'\\' ||
pszPrinter[2] == L'\\' ||
pszPrinter[uLen-1] == L'\\')))
{
Error = ERROR_INVALID_PRINTER_NAME;
}
}
SetLastError(Error);
return Error == ERROR_SUCCESS;
}
/*++
Routine Name
SplPowerEvent
Routine Description:
Checks if the spooler is ready for power management events like hibernation/stand by.
If we have printing jobs that are not in an error state or offline, then we deny the
powering down request.
Arguments:
Event - power management event
Return Value:
TRUE - the spooler allowd the system to be powered down
FALSE - the spooler denies the request for powering down
--*/
BOOL
SplPowerEvent(
DWORD Event
)
{
BOOL bAllow = TRUE;
EnterSplSem();
switch (Event)
{
case PBT_APMQUERYSUSPEND:
{
PINISPOOLER pIniSpooler;
for (pIniSpooler = pLocalIniSpooler;
pIniSpooler && bAllow;
pIniSpooler = pIniSpooler->pIniNextSpooler)
{
PINIPRINTER pIniPrinter;
for (pIniPrinter = pIniSpooler->pIniPrinter;
pIniPrinter && bAllow;
pIniPrinter = pIniPrinter->pNext)
{
PINIJOB pIniJob;
for (pIniJob = pIniPrinter->pIniFirstJob;
pIniJob && bAllow;
pIniJob = pIniJob->pIniNextJob)
{
if (pIniJob->Status & JOB_PRINTING &&
!(pIniJob->Status & JOB_ERROR | pIniJob->Status & JOB_OFFLINE))
{
bAllow = FALSE;
}
}
}
}
//
// If we allow system power down, then we need to stop scheduling jobs
//
if (bAllow)
{
ResetEvent(PowerManagementSignal);
}
break;
}
case PBT_APMQUERYSUSPENDFAILED:
case PBT_APMRESUMESUSPEND:
case PBT_APMRESUMEAUTOMATIC:
//
// Set the event to allow the spooler to continue scheudling jobs
//
SetEvent(PowerManagementSignal);
break;
default:
//
// We ignore any other power management event
//
break;
}
LeaveSplSem();
return bAllow;
}
/*++
Routine Name
IsCallViaRPC
Routine Description:
Checks if the caller of this function came in the spooler server via RPC or not.
Arguments:
None
Return Value:
TRUE - the caller came in via RPC
FALSE - the caller did not come via RPC
--*/
BOOL
IsCallViaRPC(
IN VOID
)
{
UINT uType;
return I_RpcBindingInqTransportType(NULL, &uType) == RPC_S_NO_CALL_ACTIVE ? FALSE : TRUE;
}
/*++
Routine Name
MergeMultiSz
Routine Description:
This merges two multisz strings such that there is a resulting multisz
strings that has no duplicate strings internal. This algorithm is
currently N^2 which could be improved. It is currently being called from
the driver code and the dependent files are not a large set.
Arguments:
pszMultiSz1 - The first multi-sz string.
cchMultiSz1 - The length of the multi-sz string.
pszMultiSz2 - The second multi-sz string.
cchMultiSz2 - The length of the second multi-sz string.
ppszMultiSzMerge - The merged multi-sz string.
pcchMultiSzMerge - The number of characters in the merge, this could be
less than the allocated buffer size.
Return Value:
FALSE on failure, LastError is set.
--*/
BOOL
MergeMultiSz(
IN PCWSTR pszMultiSz1,
IN DWORD cchMultiSz1,
IN PCWSTR pszMultiSz2,
IN DWORD cchMultiSz2,
OUT PWSTR *ppszMultiSzMerge,
OUT DWORD *pcchMultiSzMerge OPTIONAL
)
{
BOOL bRet = FALSE;
PWSTR pszNewMultiSz = NULL;
DWORD cchNewMultiSz = 0;
*ppszMultiSzMerge = NULL;
if (pcchMultiSzMerge)
{
*pcchMultiSzMerge = 0;
}
if (cchMultiSz1 || cchMultiSz2)
{
//
// Code assumes that these are at least 1 in the allocation size.
//
cchMultiSz1 = cchMultiSz1 == 0 ? 1 : cchMultiSz1;
cchMultiSz2 = cchMultiSz2 == 0 ? 1 : cchMultiSz2;
//
// The merged strings will be at most the size of both of them (if there are
// no duplicates).
//
pszNewMultiSz = AllocSplMem((cchMultiSz1 + cchMultiSz2 - 1) * sizeof(WCHAR));
bRet = pszNewMultiSz != NULL;
if (bRet)
{
//
// Ensure that the multi-sz string is at least empty.
//
*pszNewMultiSz = L'\0';
}
if (bRet && pszMultiSz1)
{
AddMultiSzNoDuplicates(pszMultiSz1, pszNewMultiSz);
}
if (bRet && pszMultiSz2)
{
AddMultiSzNoDuplicates(pszMultiSz2, pszNewMultiSz);
}
if (bRet)
{
cchNewMultiSz = GetMultiSZLen(pszNewMultiSz);
}
}
if (bRet)
{
*ppszMultiSzMerge = pszNewMultiSz;
if (pcchMultiSzMerge)
{
*pcchMultiSzMerge = cchNewMultiSz;
}
pszNewMultiSz = NULL;
}
FreeSplMem(pszNewMultiSz);
return bRet;
}
/*++
Routine Name
AddMultiSzNoDuplicates
Routine Description:
This adds all of the strings in a multisz string to a buffer (the buffer
must be guaranteed to be large enough to accept the strings), it makes
sure that there are no case insensitive duplicates in the list.
Arguments:
pszMultiSzIn - The multi-sz whose elements are being added.
pszNewMultiSz - The buffer in which we are filling up the multi-sz
Return Value:
None.
--*/
VOID
AddMultiSzNoDuplicates(
IN PCWSTR pszMultiSzIn,
IN OUT PWSTR pszNewMultiSz
)
{
PCWSTR pszIn = NULL;
for(pszIn = pszMultiSzIn; *pszIn; pszIn += wcslen(pszIn) + 1)
{
BOOL bStringFound = FALSE;
PWSTR pszMerge = NULL;
//
// For each input string, run the merged multi-sz string and add it if
// it is not already there.
//
for(pszMerge = pszNewMultiSz; *pszMerge; pszMerge += wcslen(pszMerge) + 1)
{
if (!_wcsicmp(pszIn, pszMerge))
{
bStringFound = TRUE;
break;
}
}
//
// If the string was not found in the multisz string, then add it to the end.
//
if (!bStringFound)
{
wcscpy(pszMerge, pszIn);
pszMerge += wcslen(pszIn) + 1;
//
// Add the extra null terminator for now.
//
*pszMerge = '\0';
}
}
}
/*++
Routine Name
GetMultiSZLen
Routine Description:
This returns the number of characters in a multisz string, including NULLs.
Arguments:
pMultiSzSrc - The multisz string to search.
Return Value:
The number of characters in the string.
--*/
DWORD
GetMultiSZLen(
IN LPWSTR pMultiSzSrc
)
{
DWORD dwLen = 0;
LPWSTR pTmp = pMultiSzSrc;
while( TRUE ) {
dwLen += wcslen(pTmp) + 1; // Incude the terminating NULL char
pTmp = pMultiSzSrc + dwLen; // Point to the beginning of the next string in the MULTI_SZ
if( !*pTmp )
return ++dwLen; // Reached the end of the MULTI_SZ string. Add 1 to the count for the last NULL char.
}
}