/*++ Copyright (c) 1990 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: --*/ #include "precomp.h" #pragma hdrstop #include "local.h" #include #include #include LPWSTR *ppszOtherNames = NULL; // Contains szMachineName, DNS name, and all other machine name forms DWORD cOtherNames = 0; // Number of other names WCHAR *gszDrvConvert = L",DrvConvert"; // // Lowercase, just like win31 for WM_WININICHANGE // WCHAR *szDevices=L"devices"; WCHAR *szWindows=L"windows"; #define NUM_INTERACTIVE_RIDS 1 extern DWORD RouterCacheSize; extern PROUTERCACHE RouterCacheTable; typedef struct _DEVMODECHG_INFO { DWORD signature; HANDLE hDrvModule; FARPROC pfnConvertDevMode; } DEVMODECHG_INFO, *PDEVMODECHG_INFO; #define DMC_SIGNATURE 'DMC' /* 'DMC' is the signature value */ DWORD RouterIsOlderThan( DWORD i, DWORD j ); VOID FreeOtherNames(LPWSTR **ppszMyOtherNames, DWORD *cOtherNames); LPWSTR AnsiToUnicodeStringWithAlloc(LPSTR pAnsi); BOOL DeleteSubKeyTree( HKEY ParentHandle, WCHAR SubKeyName[] ) { LONG Error; DWORD Index; HKEY KeyHandle; BOOL RetValue = TRUE; PWSTR pszChildKeyName = NULL; if(pszChildKeyName = AllocSplMem( sizeof(WCHAR)*MAX_PATH )) { Error = RegOpenKeyEx(ParentHandle, SubKeyName, 0, KEY_READ | KEY_WRITE, &KeyHandle); if (Error != ERROR_SUCCESS) { SetLastError(Error); RetValue = FALSE; } else { DWORD ChildKeyNameLength = MAX_PATH; // // Don't increment this Index // Index = 0; while ((Error = RegEnumKeyEx(KeyHandle, Index, pszChildKeyName, &ChildKeyNameLength, NULL, NULL, NULL, NULL )) == ERROR_SUCCESS) { RetValue = DeleteSubKeyTree( KeyHandle, pszChildKeyName ); if (RetValue == FALSE) { // // Error -- couldn't delete the sub key // break; } ChildKeyNameLength = MAX_PATH; } Error = RegCloseKey(KeyHandle); if(RetValue) { if (Error != ERROR_SUCCESS) { RetValue = FALSE; } else { Error = RegDeleteKey(ParentHandle, SubKeyName); if (Error != ERROR_SUCCESS) { RetValue = FALSE; } } } } FreeSplMem(pszChildKeyName); } else { RetValue = FALSE; SetLastError(ERROR_NOT_ENOUGH_MEMORY); } return(RetValue); } LPWSTR RemoveOrderEntry( LPWSTR szOrderString, DWORD cbStringSize, LPWSTR szOrderEntry, LPDWORD pcbBytesReturned ) { LPWSTR lpMem, psz, temp; if (szOrderString == NULL) { *pcbBytesReturned = 0; return(NULL); } if (lpMem = AllocSplMem( cbStringSize)) { DWORD cchStringLen = cbStringSize/sizeof(WCHAR); temp = szOrderString; psz = lpMem; while (*temp) { DWORD cchTempStrLen = 0; if (!lstrcmpi(temp, szOrderEntry)) // we need to remove // this entry in Order { temp += lstrlen(temp)+1; continue; } if((cchTempStrLen = lstrlen(temp)+1) > cchStringLen) { break; } else { StringCchCopy(psz,cchStringLen,temp); cchStringLen -= cchTempStrLen; psz += cchTempStrLen; temp += cchTempStrLen; } } *psz = L'\0'; *pcbBytesReturned = (DWORD) ((psz - lpMem)+1)*sizeof(WCHAR); return(lpMem); } *pcbBytesReturned = 0; return(lpMem); } LPWSTR AppendOrderEntry( LPWSTR szOrderString, DWORD cbStringSize, LPWSTR szOrderEntry, LPDWORD pcbBytesReturned ) { LPWSTR lpMem, temp, psz; DWORD cb = 0; DWORD cchStrLen = 0; BOOL bExists = FALSE; if ((szOrderString == NULL) && (szOrderEntry == NULL)) { *pcbBytesReturned = 0; return(NULL); } if (szOrderString == NULL) { cb = wcslen(szOrderEntry)*sizeof(WCHAR)+ sizeof(WCHAR) + sizeof(WCHAR); if (lpMem = AllocSplMem(cb)){ StringCbCopy(lpMem, cb, szOrderEntry); *pcbBytesReturned = cb; } else { *pcbBytesReturned = 0; } return lpMem; } cchStrLen = cbStringSize + wcslen(szOrderEntry) + 1; if (lpMem = AllocSplMem(cchStrLen * sizeof(WCHAR))){ temp = szOrderString; psz = lpMem; while (*temp) { DWORD cchTempStrLen = 0; if (!lstrcmpi(temp, szOrderEntry)) { // // Make sure we don't // duplicate entries // bExists = TRUE; } if((cchTempStrLen = lstrlen(temp)+1) > cchStrLen) { break; } else { StringCchCopy(psz,cchStrLen,temp); cchStrLen -= cchTempStrLen; psz += cchTempStrLen; temp += cchTempStrLen; } } if (!bExists) { // // if it doesn't exist // add the entry // StringCchCopy(psz, cchStrLen, szOrderEntry); psz += min(cchStrLen,(DWORD)(lstrlen(szOrderEntry)+1)); } *psz = L'\0'; // the second null character *pcbBytesReturned = (DWORD) ((psz - lpMem) + 1)* sizeof(WCHAR); } return(lpMem); } typedef struct { DWORD dwType; DWORD dwMessage; WPARAM wParam; LPARAM lParam; } MESSAGE, *PMESSAGE; VOID SendMessageThread( PMESSAGE pMessage); BOOL BroadcastMessage( DWORD dwType, DWORD dwMessage, WPARAM wParam, LPARAM lParam) { HANDLE hThread; DWORD ThreadId; PMESSAGE pMessage; BOOL bReturn = FALSE; pMessage = AllocSplMem(sizeof(MESSAGE)); if (pMessage) { pMessage->dwType = dwType; pMessage->dwMessage = dwMessage; pMessage->wParam = wParam; pMessage->lParam = lParam; // // We should have a queue of events to broadcast and then have a // single thread pulling them off the queue until there is nothing // left and then that thread could go away. // // The current design can lead to a huge number of threads being // created and torn down in both this and csrss process. // hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)SendMessageThread, (LPVOID)pMessage, 0, &ThreadId); if (hThread) { CloseHandle(hThread); bReturn = TRUE; } else { FreeSplMem(pMessage); } } return bReturn; } // 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 pared 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. VOID SendMessageThread( PMESSAGE pMessage) { switch (pMessage->dwType) { case BROADCAST_TYPE_MESSAGE: SendNotifyMessage(HWND_BROADCAST, pMessage->dwMessage, pMessage->wParam, pMessage->lParam); break; case BROADCAST_TYPE_CHANGEDEFAULT: // // Same order and strings as win31. // SendNotifyMessage(HWND_BROADCAST, WM_WININICHANGE, 0, (LPARAM)szDevices); SendNotifyMessage(HWND_BROADCAST, WM_WININICHANGE, 0, (LPARAM)szWindows); break; } FreeSplMem(pMessage); ExitThread(0); } BOOL IsNamedPipeRpcCall( VOID ) { unsigned int uType; // // // return ERROR_SUCCESS == I_RpcBindingInqTransportType(NULL, &uType) && uType != TRANSPORT_TYPE_LPC; } /*++ Name: CheckLocalCall Description: This function checks whether the current thread is handling a local or a remote call. Definiton of a remote call: -the call came via a transport different from LPC or -the came came via LPC, but the thread's security token includes the SECURITY_NETWORK_RID Arguments: None. Return Value: S_OK - local call S_FALSE - remote call other HR - an error occurred and the call type could not be determined --*/ HRESULT CheckLocalCall( VOID ) { HRESULT hResult = S_OK; DWORD SaveLastError = GetLastError(); unsigned int uType; DWORD Error = I_RpcBindingInqTransportType(NULL, &uType); switch (Error) { case RPC_S_NO_CALL_ACTIVE: { // // KM call // hResult = S_OK; break; } case ERROR_SUCCESS: { if (uType != TRANSPORT_TYPE_LPC) { // // Not LRPC so call is remote // hResult = S_FALSE; } else { HANDLE hToken; if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken)) { SID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY; PSID pTestSid = NULL; BOOL bMember; if (AllocateAndInitializeSid(&sia, 1, SECURITY_NETWORK_RID, 0, 0, 0, 0, 0, 0, 0, &pTestSid)) { if (CheckTokenMembership(hToken, pTestSid, &bMember)) { hResult = bMember ? S_FALSE : S_OK; } else { hResult = GetLastErrorAsHResult(); } FreeSid(pTestSid); } else { hResult = GetLastErrorAsHResult(); } CloseHandle(hToken); } else { hResult = GetLastErrorAsHResult(); // // The following call originates from the print processor. There are port monitors which // will revert to spooler self before calling open printer. In this case, OpenThreadToken // fails. We need to treat ERROR_NO_TOKEN as call coming from the local machine. // // localspl!SplOpenPrinter // localspl!LocalOpenPrinterEx+0x7b // SPOOLSS!RouterOpenPrinterW+0x13f // SPOOLSS!OpenPrinterW+0x17 // SOMEPORTMON!pfnStartDocPort // localspl!PrintingDirectlyToPort+0x199 // localspl!LocalStartDocPrinter+0x4e // SPOOLSS!StartDocPrinterW+0x21 // spoolsv!YStartDocPrinter+0xaf // spoolsv!RpcStartDocPrinter+0x13 // RPCRT4!something // if (hResult == HRESULT_FROM_WIN32(ERROR_NO_TOKEN)) { hResult = S_OK; } } } break; } default: { hResult = HRESULT_FROM_WIN32(Error); } } SetLastError(SaveLastError); return hResult; } LPPROVIDOR FindEntryinRouterCache( LPWSTR pPrinterName ) { DWORD i; if (!pPrinterName) return NULL; DBGMSG(DBG_TRACE, ("FindEntryinRouterCache with %ws\n", pPrinterName)); if (RouterCacheSize == 0 ) { DBGMSG(DBG_TRACE, ("FindEntryInRouterCache with %ws returning -1 (zero cache)\n", pPrinterName)); return NULL; } for (i = 0; i < RouterCacheSize; i++ ) { if (RouterCacheTable[i].bAvailable) { if (!_wcsicmp(RouterCacheTable[i].pPrinterName, pPrinterName)) { // // update the time stamp so that it is current and not old // GetSystemTime(&RouterCacheTable[i].st); // // // DBGMSG(DBG_TRACE, ("FindEntryinRouterCache returning with %d\n", i)); return RouterCacheTable[i].pProvidor; } } } DBGMSG(DBG_TRACE, ("FindEntryinRouterCache returning with -1\n")); return NULL; } DWORD AddEntrytoRouterCache( LPWSTR pPrinterName, LPPROVIDOR pProvidor ) { DWORD LRUEntry = (DWORD)-1; DWORD i; DBGMSG(DBG_TRACE, ("AddEntrytoRouterCache with %ws\n", pPrinterName)); if (RouterCacheSize == 0 ) { DBGMSG(DBG_TRACE, ("AddEntrytoRouterCache with %ws returning -1 (zero cache)\n", pPrinterName)); return (DWORD)-1; } for (i = 0; i < RouterCacheSize; i++ ) { if (!RouterCacheTable[i].bAvailable) { // // Found an available entry; use it // fill in the name of the printer and the providor // that supports this printer. // break; } else { if ((LRUEntry == -1) || (i == RouterIsOlderThan(i, LRUEntry))){ LRUEntry = i; } } } if (i == RouterCacheSize) { // // We have no available entries so we need to use // the LRUEntry which is busy // FreeSplStr(RouterCacheTable[LRUEntry].pPrinterName); RouterCacheTable[LRUEntry].bAvailable = FALSE; i = LRUEntry; } if ((RouterCacheTable[i].pPrinterName = AllocSplStr(pPrinterName)) == NULL){ // // Alloc failed so we're kinda hosed so return -1 // return (DWORD)-1; } RouterCacheTable[i].bAvailable = TRUE; RouterCacheTable[i].pProvidor = pProvidor; // // update the time stamp so that we know when this entry was made // GetSystemTime(&RouterCacheTable[i].st); DBGMSG(DBG_TRACE, ("AddEntrytoRouterCache returning with %d\n", i)); return i; } VOID DeleteEntryfromRouterCache( LPWSTR pPrinterName ) { DWORD i; if (RouterCacheSize == 0) { DBGMSG(DBG_TRACE, ("DeleteEntryfromRouterCache with %ws returning -1 (zero cache)\n", pPrinterName)); return; } DBGMSG(DBG_TRACE, ("DeleteEntryFromRouterCache with %ws\n", pPrinterName)); for (i = 0; i < RouterCacheSize; i++ ) { if (RouterCacheTable[i].bAvailable) { if (!_wcsicmp(RouterCacheTable[i].pPrinterName, pPrinterName)) { // // reset the available flag on this node // FreeSplStr(RouterCacheTable[i].pPrinterName); RouterCacheTable[i].pProvidor = NULL; RouterCacheTable[i].bAvailable = FALSE; DBGMSG(DBG_TRACE, ("DeleteEntryFromRouterCache returning after deleting the %d th entry\n", i)); return; } } } DBGMSG(DBG_TRACE, ("DeleteEntryFromRouterCache returning after not finding an entry to delete\n")); } DWORD RouterIsOlderThan( DWORD i, DWORD j ) { SYSTEMTIME *pi, *pj; DWORD iMs, jMs; DBGMSG(DBG_TRACE, ("RouterIsOlderThan entering with i %d j %d\n", i, j)); pi = &(RouterCacheTable[i].st); pj = &(RouterCacheTable[j].st); DBGMSG(DBG_TRACE, ("Index i %d - %d:%d:%d:%d:%d:%d:%d\n", i, pi->wYear, pi->wMonth, pi->wDay, pi->wHour, pi->wMinute, pi->wSecond, pi->wMilliseconds)); DBGMSG(DBG_TRACE,("Index j %d - %d:%d:%d:%d:%d:%d:%d\n", j, pj->wYear, pj->wMonth, pj->wDay, pj->wHour, pj->wMinute, pj->wSecond, pj->wMilliseconds)); if (pi->wYear < pj->wYear) { DBGMSG(DBG_TRACE, ("RouterIsOlderThan returns %d\n", i)); return(i); } else if (pi->wYear > pj->wYear) { DBGMSG(DBG_TRACE, ("RouterIsOlderThan returns %d\n", j)); return(j); } else if (pi->wMonth < pj->wMonth) { DBGMSG(DBG_TRACE, ("RouterIsOlderThan returns %d\n", i)); return(i); } else if (pi->wMonth > pj->wMonth) { DBGMSG(DBG_TRACE, ("RouterIsOlderThan returns %d\n", j)); return(j); } else if (pi->wDay < pj->wDay) { DBGMSG(DBG_TRACE, ("RouterIsOlderThan returns %d\n", i)); return(i); } else if (pi->wDay > pj->wDay) { DBGMSG(DBG_TRACE, ("RouterIsOlderThan returns %d\n", j)); return(j); } else { iMs = ((((pi->wHour * 60) + pi->wMinute)*60) + pi->wSecond)* 1000 + pi->wMilliseconds; jMs = ((((pj->wHour * 60) + pj->wMinute)*60) + pj->wSecond)* 1000 + pj->wMilliseconds; if (iMs <= jMs) { DBGMSG(DBG_TRACE, ("RouterIsOlderThan returns %d\n", i)); return(i); } else { DBGMSG(DBG_TRACE, ("RouterIsOlderThan returns %d\n", j)); return(j); } } } /*++ Routine Name ImpersonationToken Routine Description: This routine checks if a token is a primary token or an impersonation token. Arguments: hToken - impersonation token or primary token of the process Return Value: TRUE, if the token is an impersonation token FALSE, otherwise. --*/ BOOL ImpersonationToken( IN HANDLE hToken ) { BOOL bRet = TRUE; TOKEN_TYPE eTokenType; DWORD cbNeeded; DWORD LastError; // // Preserve the last error. Some callers of ImpersonatePrinterClient (which // calls ImpersonationToken) rely on the fact that ImpersonatePrinterClient // does not alter the last error. // LastError = GetLastError(); // // Get the token type from the thread token. The token comes // from RevertToPrinterSelf. An impersonation token cannot be // queried, because RevertToPRinterSelf doesn't open it with // TOKEN_QUERY access. That's why we assume that hToken is // an impersonation token by default // if (GetTokenInformation(hToken, TokenType, &eTokenType, sizeof(eTokenType), &cbNeeded)) { bRet = eTokenType == TokenImpersonation; } SetLastError(LastError); return bRet; } /*++ Routine Name RevertToPrinterSelf Routine Description: This routine will revert to the local system. It returns the token that ImpersonatePrinterClient then uses to imersonate the client again. If the current thread doesn't impersonate, then the function merely returns the primary token of the process. (instead of returning NULL) Thus we honor a request for reverting to printer self, even if the thread is not impersonating. Arguments: None. Return Value: NULL, if the function failed HANDLE to token, otherwise. --*/ HANDLE RevertToPrinterSelf( VOID ) { HANDLE NewToken, OldToken; NTSTATUS Status; NewToken = NULL; Status = NtOpenThreadToken(NtCurrentThread(), TOKEN_IMPERSONATE, TRUE, &OldToken); if (NT_SUCCESS(Status)) { // // We are currently impersonating // Status = NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, (PVOID)&NewToken, (ULONG)sizeof(HANDLE)); } else if (Status == STATUS_NO_TOKEN) { // // We are not impersonating // Status = NtOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &OldToken); } if (!NT_SUCCESS(Status)) { SetLastError(Status); return NULL; } return OldToken; } /*++ Routine Name ImpersonatePrinterClient Routine Description: This routine attempts to set the passed in hToken as the token for the current thread. If hToken is not an impersonation token, then the routine will simply close the token. Arguments: hToken - impersonation token or primary token of the process Return Value: TRUE, if the function succeeds in setting hToken FALSE, otherwise. --*/ BOOL ImpersonatePrinterClient( HANDLE hToken) { NTSTATUS Status; // // Check if we have an impersonation token // if (ImpersonationToken(hToken)) { Status = NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, (PVOID)&hToken, (ULONG)sizeof(HANDLE)); if (!NT_SUCCESS(Status)) { SetLastError(Status); return FALSE; } } NtClose(hToken); return TRUE; } HANDLE LoadDriver( LPWSTR pDriverFile) { UINT uOldErrorMode; fnWinSpoolDrv fnList; HANDLE hReturn = NULL; if (!pDriverFile || !*pDriverFile) { // Nothing to load return hReturn; } uOldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); if (SplInitializeWinSpoolDrv(&fnList)) { hReturn = (*(fnList.pfnRefCntLoadDriver))(pDriverFile, LOAD_WITH_ALTERED_SEARCH_PATH, 0, FALSE); } (VOID)SetErrorMode(uOldErrorMode); return hReturn; } VOID UnloadDriver( HANDLE hModule ) { fnWinSpoolDrv fnList; if (SplInitializeWinSpoolDrv(&fnList)) { (* (fnList.pfnRefCntUnloadDriver))(hModule, TRUE); } } VOID UnloadDriverFile( IN OUT HANDLE hDevModeChgInfo ) /*++ Description: Does a FreeLibrary on the driver file and frees memory Arguments: hDevModeChgInfo - A handle returned by LoadDriverFiletoConvertDevmode Return Value: None --*/ { PDEVMODECHG_INFO pDevModeChgInfo = (PDEVMODECHG_INFO) hDevModeChgInfo; SPLASSERT(pDevModeChgInfo && pDevModeChgInfo->signature == DMC_SIGNATURE); if ( pDevModeChgInfo && pDevModeChgInfo->signature == DMC_SIGNATURE ) { if ( pDevModeChgInfo->hDrvModule ) { UnloadDriver(pDevModeChgInfo->hDrvModule); } FreeSplMem((LPVOID)pDevModeChgInfo); } } HANDLE LoadDriverFiletoConvertDevmode( IN LPWSTR pDriverFile ) /*++ Description: Does a LoadLibrary on the driver file given. This will give a handle which can be used to do devmode conversion later using CallDrvDevModeConversion. Caller should call UnloadDriverFile to do a FreeLibrary and free memory Note: Driver will call OpenPrinter to spooler Arguments: pDriverFile - Full path of the driver file to do a LoadLibrary Return Value: A handle value to be used to make calls to CallDrvDevModeConversion, NULL on error --*/ { PDEVMODECHG_INFO pDevModeChgInfo = NULL; BOOL bFail = TRUE; DWORD dwNeeded; SPLASSERT(pDriverFile != NULL); pDevModeChgInfo = (PDEVMODECHG_INFO) AllocSplMem(sizeof(*pDevModeChgInfo)); if ( !pDevModeChgInfo ) { DBGMSG(DBG_WARNING, ("printer.c: Memory allocation failed for DEVMODECHG_INFO\n")); goto Cleanup; } pDevModeChgInfo->signature = DMC_SIGNATURE; pDevModeChgInfo->hDrvModule = LoadDriver(pDriverFile); if ( !pDevModeChgInfo->hDrvModule ) { DBGMSG(DBG_WARNING,("LoadDriverFiletoConvertDevmode: Can't load driver file %ws\n", pDriverFile)); goto Cleanup; } // // Some third party driver may not be providing DrvConvertDevMode // pDevModeChgInfo->pfnConvertDevMode = GetProcAddress(pDevModeChgInfo->hDrvModule, "DrvConvertDevMode"); if ( !pDevModeChgInfo->pfnConvertDevMode ) goto Cleanup; bFail = FALSE; Cleanup: if ( bFail ) { if ( pDevModeChgInfo ) { UnloadDriverFile((HANDLE)pDevModeChgInfo); } return (HANDLE) NULL; } else { return (HANDLE) pDevModeChgInfo; } } DWORD CallDrvDevModeConversion( IN HANDLE hDevModeChgInfo, IN LPWSTR pszPrinterName, IN LPBYTE pDevMode1, IN OUT LPBYTE *ppDevMode2, IN OUT LPDWORD pdwOutDevModeSize, IN DWORD dwConvertMode, IN BOOL bAlloc ) /*++ Description: Does deve mode conversion by calling driver If bAlloc is TRUE routine will do the allocation using AllocSplMem Note: Driver is going to call OpenPrinter. Arguments: hDevModeChgInfo - Points to DEVMODECHG_INFO pszPrinterName - Printer name pInDevMode - Input devmode (will be NULL for CDM_DRIVER_DEFAULT) *pOutDevMode - Points to output devmode pdwOutDevModeSize - Output devmode size on succesful return if !bAlloc this will give input buffer size dwConvertMode - Devmode conversion mode to give to driver bAllocate - Tells the routine to do allocation to *pOutPrinter If bAllocate is TRUE and no devmode conversion is required call will fail. Return Value: Returns last error --*/ { DWORD dwBufSize, dwSize, dwLastError = ERROR_SUCCESS; LPDEVMODE pInDevMode = (LPDEVMODE)pDevMode1, *ppOutDevMode = (LPDEVMODE *) ppDevMode2; PDEVMODECHG_INFO pDevModeChgInfo = (PDEVMODECHG_INFO) hDevModeChgInfo; PWSTR pszDrvConvert = pszPrinterName; if ( !pDevModeChgInfo || pDevModeChgInfo->signature != DMC_SIGNATURE || !pDevModeChgInfo->pfnConvertDevMode ) { SPLASSERT(pDevModeChgInfo && pDevModeChgInfo->signature == DMC_SIGNATURE && pDevModeChgInfo->pfnConvertDevMode); return ERROR_INVALID_PARAMETER; } // // We decorate the pszPrinterName with ",DrvConvert" to prevent drivers from // infinitely recursing by calling GetPrinter inside ConvertDevMode // if (wcsstr(pszPrinterName, gszDrvConvert)) { return ERROR_INVALID_PARAMETER; } if (!(pszDrvConvert = AutoCat(pszPrinterName, gszDrvConvert))) { return GetLastError(); } DBGMSG(DBG_INFO,("Convert DevMode %d\n", dwConvertMode)); #if DBG #else try { #endif if ( bAlloc ) { // // If we have to do allocation first find size neeeded // *pdwOutDevModeSize = 0; *ppOutDevMode = NULL; (*pDevModeChgInfo->pfnConvertDevMode)(pszDrvConvert, pInDevMode, NULL, pdwOutDevModeSize, dwConvertMode); dwLastError = GetLastError(); if ( dwLastError != ERROR_INSUFFICIENT_BUFFER ) { DBGMSG(DBG_WARNING, ("CallDrvDevModeConversion: Unexpected error %d\n", GetLastError())); if (dwLastError == ERROR_SUCCESS) { SPLASSERT(dwLastError != ERROR_SUCCESS); // if driver doesn't fail the above call, it is a broken driver and probably // failed a HeapAlloc, which doesn't SetLastError() SetLastError(dwLastError = ERROR_NOT_ENOUGH_MEMORY); } #if DBG goto Cleanup; #else leave; #endif } *ppOutDevMode = AllocSplMem(*pdwOutDevModeSize); if ( !*ppOutDevMode ) { dwLastError = GetLastError(); #if DBG goto Cleanup; #else leave; #endif } } dwBufSize = *pdwOutDevModeSize; if ( !(*pDevModeChgInfo->pfnConvertDevMode)( pszDrvConvert, pInDevMode, ppOutDevMode ? *ppOutDevMode : NULL, pdwOutDevModeSize, dwConvertMode) ) { dwLastError = GetLastError(); if (dwLastError == ERROR_SUCCESS) { SPLASSERT(dwLastError != ERROR_SUCCESS); // if driver doesn't fail the above call, it is a broken driver and probably // failed a HeapAlloc, which doesn't SetLastError() SetLastError(dwLastError = ERROR_NOT_ENOUGH_MEMORY); } } else { dwLastError = StatusFromHResult(SplIsValidDevmodeW(*ppOutDevMode, *pdwOutDevModeSize)); } #if DBG Cleanup: #else } except(1) { DBGMSG(DBG_ERROR, ("CallDrvDevModeConversion: Exception from driver\n")); dwLastError = GetExceptionCode(); SetLastError(dwLastError); } #endif // // If we allocated mmeory free it and zero the pointer // if ( dwLastError != ERROR_SUCCESS && bAlloc && *ppOutDevMode ) { FreeSplMem(*ppOutDevMode); *ppOutDevMode = 0; *pdwOutDevModeSize = 0; } if ( dwLastError != ERROR_SUCCESS && dwLastError != ERROR_INSUFFICIENT_BUFFER ) { DBGMSG(DBG_WARNING, ("DevmodeConvert unexpected error %d\n", dwLastError)); } if ( dwLastError == ERROR_SUCCESS ) { dwSize = (*ppOutDevMode)->dmSize + (*ppOutDevMode)->dmDriverExtra; // // Did the driver return correct size as per the devmode? // if ( *pdwOutDevModeSize != dwSize ) { DBGMSG(DBG_ERROR, ("Driver says outsize is %d, really %d\n", *pdwOutDevModeSize, dwSize)); *pdwOutDevModeSize = dwSize; } // // Is it a valid devmode which did not overwrite the buffer? // if ( *pdwOutDevModeSize < MIN_DEVMODE_SIZEW || *pdwOutDevModeSize > dwBufSize ) { DBGMSG(DBG_ERROR, ("Bad devmode from the driver size %d, buffer %d", *pdwOutDevModeSize, dwBufSize)); dwLastError = ERROR_INVALID_PARAMETER; if ( bAlloc ) { FreeSplMem(*ppOutDevMode); *ppOutDevMode = NULL; } *pdwOutDevModeSize = 0; } } FreeSplMem(pszDrvConvert); return dwLastError; } BOOL BuildOtherNamesFromMachineName( LPWSTR **ppszMyOtherNames, DWORD *cOtherNames ) /*++ Routine Description: This routine builds list of names other than the machine name that can be used to call spooler APIs. --*/ { HANDLE hModule; struct hostent *HostEnt, *(*Fngethostbyname)(LPTSTR); struct in_addr *ptr; INT (*FnWSAStartup)(WORD, LPWSADATA); DWORD Index, Count; WSADATA WSAData; VOID (*FnWSACleanup)(); LPSTR (*Fninet_ntoa)(struct in_addr); WORD wVersion; BOOL bRet = FALSE; DWORD dwRet; SPLASSERT(cOtherNames && ppszMyOtherNames); *cOtherNames = 0; wVersion = MAKEWORD(1, 1); dwRet = WSAStartup(wVersion, &WSAData); if (dwRet) { DBGMSG(DBG_WARNING, ("BuildOtherNamesFromMachineName: WSAStartup failed\n")); SetLastError(dwRet); return FALSE; } HostEnt = gethostbyname(NULL); if (HostEnt) { for (*cOtherNames = 0 ; HostEnt->h_addr_list[*cOtherNames] ; ++(*cOtherNames)) ; *cOtherNames += 2; // Add one for DNS and one for machine name *ppszMyOtherNames = (LPWSTR *) AllocSplMem(*cOtherNames*sizeof *ppszMyOtherNames); if ( !*ppszMyOtherNames ) { *cOtherNames = 0; goto Cleanup; } (*ppszMyOtherNames)[0] = AllocSplStr(szMachineName + 2); // Exclude the leading double-backslash (*ppszMyOtherNames)[1] = AnsiToUnicodeStringWithAlloc(HostEnt->h_name); for (Index = 0 ; HostEnt->h_addr_list[Index] ; ++Index) { ptr = (struct in_addr *) HostEnt->h_addr_list[Index]; (*ppszMyOtherNames)[Index+2] = AnsiToUnicodeStringWithAlloc(inet_ntoa(*ptr)); } // check for allocation failures for (Index = 0 ; Index < *cOtherNames ; ++Index) { if ( !(*ppszMyOtherNames)[Index] ) { FreeOtherNames(ppszMyOtherNames, cOtherNames); SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto Cleanup; } } bRet = TRUE; } else { DBGMSG(DBG_WARNING, ("BuildOtherNamesFromMachineName: gethostbyname failed for with %d\n", GetLastError())); } Cleanup: WSACleanup(); return bRet; } VOID FreeOtherNames(LPWSTR **ppszMyOtherNames, DWORD *cOtherNames) { DWORD i; for( i = 0 ; i < *cOtherNames ; ++i) FreeSplMem((*ppszMyOtherNames)[i]); FreeSplMem(*ppszMyOtherNames); } 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; } } /*++ Routine Description Determines whether or not a machine name contains the local machine name. Localspl enum calls fail if pName != local machine name (\\Machine). Remote enum provider is then called. The remote enum provider must check if the UNC name refers to the local machine, and fail if it does to avoid endless recursion. Arguments: LPWSTR pName - UNC name. Return Value: TRUE: pName == \\szMachineName\... - or - pName == \\szMachineName FALSE: anything else Author: swilson --*/ BOOL MyUNCName( LPWSTR pNameStart ) { PWCHAR pMachine = szMachineName; LPWSTR pName; DWORD i; extern LPWSTR *ppszOtherNames; // Contains szMachineName, DNS name, and all other machine name forms extern DWORD cOtherNames; if (!pNameStart || !*pNameStart) // This differs from MyName(), which returns TRUE return FALSE; if (*pNameStart == L'\\' && *(pNameStart + 1) == L'\\') { for (i = 0 , pName = pNameStart + 2 ; i < cOtherNames ; ++i , pName = pNameStart + 2) { for(pMachine = ppszOtherNames[i] ; *pName && towupper(*pName) == towupper(*pMachine) ; ++pName, ++pMachine) ; if(!*pMachine && (!*pName || *pName == L'\\')) return TRUE; } } return FALSE; } BOOL SplIsUpgrade( VOID ) { return dwUpgradeFlag; } PWSTR AutoCat( PCWSTR pszInput, PCWSTR pszCat ) { PWSTR pszOut; if (!pszCat) { pszOut = AllocSplStr(pszInput); } else if (pszInput) { DWORD cchOutStr = wcslen(pszInput) + wcslen(pszCat) + 1; pszOut = AllocSplMem(cchOutStr * sizeof(WCHAR)); if (pszOut) { StrNCatBuff(pszOut, cchOutStr, pszInput, pszCat, NULL); } } else { pszOut = AllocSplStr(pszCat); } return pszOut; } PBIDI_RESPONSE_CONTAINER RouterAllocBidiResponseContainer( DWORD Count ) { DWORD MemSize = 0; // // Add the size of the container - the size of the first data element // MemSize += (sizeof(BIDI_RESPONSE_CONTAINER) - sizeof(BIDI_RESPONSE_DATA)); // // Add the size of all the returned RESPONSE_DATA elements // MemSize += (Count * sizeof(BIDI_RESPONSE_DATA)); return((PBIDI_RESPONSE_CONTAINER) MIDL_user_allocate(MemSize)); } /*++ Routine Name GetAPDPolicy Routine Description: This function reads a DWORD value from the location HKEY\pszRelPath\pszValueName. We use this function for preserving the AddPrinterDrivers policy value when the LanMan Print Services print provider is deleted from the system. Arguments: hKey - key tree pszRelPath - relative path of the value to be get pszValueName - value name pValue - pointer to memory to store a dword value Return Value: ERROR_SUCCESS - the value was retrieved Win32 error code - an error occured --*/ DWORD GetAPDPolicy( IN HKEY hKey, IN LPCWSTR pszRelPath, IN LPCWSTR pszValueName, IN LPDWORD pValue ) { DWORD Error = ERROR_INVALID_PARAMETER; if (hKey && pszRelPath && pszValueName && pValue) { HKEY hRelKey = NULL; *pValue = 0; // // Check if we have a value in the new location already // if ((Error = RegOpenKeyEx(hKey, pszRelPath, 0, KEY_READ, &hRelKey)) == ERROR_SUCCESS) { DWORD cbData = sizeof(DWORD); Error = RegQueryValueEx(hRelKey, pszValueName, NULL, NULL, (LPBYTE)pValue, &cbData); RegCloseKey(hRelKey); } } return Error; } /*++ Routine Name SetAPDPolicy Routine Description: This function writes a DWORD value to the location HKEY\pszRelPath\pszValueName. We use this function for preserving the AddPrinterDrivers policy value when the LanMan Print Services print provider is deleted from the system. Arguments: hKey - key tree pszRelPath - relative path of the value to be set pszValueName - value name to be set Value - dword value to be set Return Value: ERROR_SUCCESS - the value was set sucessfully Win32 error code - an error occured and the value was not set --*/ DWORD SetAPDPolicy( IN HKEY hKey, IN LPCWSTR pszRelPath, IN LPCWSTR pszValueName, IN DWORD Value ) { DWORD Error = ERROR_INVALID_PARAMETER; if (hKey && pszRelPath && pszValueName) { HKEY hRelKey = NULL; // // Check if we have a value in the new location already // if ((Error = RegCreateKeyEx(hKey, pszRelPath, 0, NULL, 0, KEY_SET_VALUE, NULL, &hRelKey, NULL)) == ERROR_SUCCESS) { Error = RegSetValueEx(hRelKey, pszValueName, 0, REG_DWORD, (LPBYTE)&Value, sizeof(DWORD)); RegCloseKey(hRelKey); } } return Error; }