/*++ 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: Muhunthan Sivapragasam ( MuhuntS ) 5-June-1995 Moved from printer.c: RegSetBinaryData RegSetString RegSetDWord Wrote: SameDependentFileswcsicmpEx wcsicmpEx RegGetValue Matthew A Felton ( MattFe ) 23-mar-1995 DeleteAllFilesAndDirectory DeleteAllFilesInDirectory CreateDirectoryWithoutImpersonatingUser --*/ #include #include #include extern PWCHAR pszUpgradeToken; DWORD dwBeepEnabled = 0; DWORD BroadcastCount = 0; CRITICAL_SECTION SpoolerSection; PDBG_POINTERS gpDbgPointers = NULL; #if DBG HANDLE hcsSpoolerSection = NULL; VOID SplInSem( VOID ) { if( hcsSpoolerSection ){ SPLASSERT( gpDbgPointers->pfnInsideCritSec( hcsSpoolerSection )); } else { SPLASSERT( (DWORD)SpoolerSection.OwningThread == GetCurrentThreadId( )); } } VOID SplOutSem( VOID ) { if( hcsSpoolerSection ){ SPLASSERT( gpDbgPointers->pfnOutsideCritSec( hcsSpoolerSection )); } else { SPLASSERT( (DWORD)SpoolerSection.OwningThread != 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( LPWSTR pFileName ) { // // NULL or "" is OK: // return (pFileName && *pFileName) ? FileExists(pFileName) : TRUE; } DWORD GetFullNameFromId( PINIPRINTER pIniPrinter, DWORD JobId, BOOL fJob, LPWSTR pFileName, BOOL Remote ) { DWORD i; i = GetPrinterDirectory(pIniPrinter, Remote, pFileName, 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, 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 psz = pIniSpooler->pDir; while (pDir[i++]=*psz++) ; pDir[i-1]=L'\\'; psz = szPrinterDir; while (pDir[i++]=*psz++) ; 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, PINIENVIRONMENT pIniEnvironment, BOOL Remote, PINISPOOLER pIniSpooler ) { DWORD i=0; LPWSTR psz; if (Remote) { psz = pIniSpooler->pszDriversShare; while (pDir[i++]=*psz++) ; } else { psz = pIniSpooler->pDir; while (pDir[i++]=*psz++) ; pDir[i-1]=L'\\'; psz = szDriverDir; while (pDir[i++]=*psz++) ; } pDir[i-1]=L'\\'; psz = pIniEnvironment->pDirectory; while (pDir[i++]=*psz++) ; return i-1; } DWORD GetProcessorDirectory( LPWSTR pDir, LPWSTR pEnvironment, PINISPOOLER pIniSpooler ) { DWORD i=0; LPWSTR psz; psz = pIniSpooler->pDir; while (pDir[i++]=*psz++) ; pDir[i-1]=L'\\'; psz = szPrintProcDir; while (pDir[i++]=*psz++) ; pDir[i-1]=L'\\'; psz = pEnvironment; while (pDir[i++]=*psz++) ; return i-1; } 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; } LPWSTR FindFileName( LPWSTR pPathName ) { LPWSTR pSlash; LPWSTR pTemp; if (pPathName == NULL) { return(NULL); } pTemp = pPathName; while (pSlash = wcschr(pTemp, L'\\')) { pTemp = pSlash+1; } if (!*pTemp) { return(NULL); } return(pTemp); } LPWSTR GetFileName( LPWSTR pPathName ) { LPWSTR pFileName; pFileName = FindFileName( pPathName ); if (pFileName) { return( AllocSplStr( pFileName ) ); } else { return(NULL); } } LPWSTR 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); return pPathName; } } } FreeSplMem(pPathName); } return FALSE; } 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; // Remember, wcstok has the side effect of replacing the delimiter // by NULL, which is precisely what we want: psz = wcstok (pDest, L","); while (psz) { *ppToken++ = psz; psz = wcstok (NULL, L","); } 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 (pIniPort = pIniPrinter->pIniSpooler->pIniPort ; pIniPort ; pIniPort = pIniPort->pNext) { if (!(pIniPort->Status & PP_FILE)) { for (i = 0 ; i < pIniPort->cPrinters ; i++) { if (pIniPort->ppIniPrinter[i] == pIniPrinter) { if (Comma) cbNeeded += wcslen(szComma)*sizeof(WCHAR); cbNeeded += wcslen(pIniPort->pName)*sizeof(WCHAR); Comma = TRUE; } } } } cbNeeded += sizeof(WCHAR); // Add in size of NULL if (cbNeeded <= *pcbNeeded) { // If we are given a buffer & buffer is big enough, then fill it SPLASSERT(pszPorts); Comma = FALSE; for (pIniPort = pIniPrinter->pIniSpooler->pIniPort ; pIniPort ; pIniPort = pIniPort->pNext) { if (!(pIniPort->Status & PP_FILE)) { for (i = 0; i < pIniPort->cPrinters; i++) { if (pIniPort->ppIniPrinter[i] == pIniPrinter) { if (Comma) { wcscat(pszPorts, szComma); wcscat(pszPorts, pIniPort->pName); } else { wcscpy(pszPorts, pIniPort->pName); } Comma = TRUE; } } } } } *pcbNeeded = cbNeeded; } BOOL MyName( 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; } } SetLastError( ERROR_INVALID_NAME ); return FALSE; } BOOL GetSid( PHANDLE phToken ) { if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, 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 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]; WCHAR Buffer2[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; } if (FormatMessage(dwFlags, hModule, Error, 0, Buffer1, sizeof(Buffer1), NULL)) { EnterSplSem(); pErrorString = AllocSplStr(Buffer1); LeaveSplSem(); } else { if (LoadString(hInst, IDS_UNRECOGNIZED_ERROR, Buffer1, sizeof Buffer1 / sizeof *Buffer1) && wsprintf(Buffer2, Buffer1, Error, Error)) { EnterSplSem(); pErrorString = AllocSplStr(Buffer2); 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[256]; 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 ); wvsprintf( MsgText, MsgFormat, vargs ); 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 SendMessageThread( PMESSAGE pMessage ) { SendNotifyMessage(HWND_BROADCAST, pMessage->Message, pMessage->wParam, pMessage->lParam); if ( pMessage->Message == WM_DEVMODECHANGE ){ // // TESTING // ++dwSendFormMessage; FreeSplStr( (LPWSTR)pMessage->lParam ); } FreeSplMem(pMessage); ExitThread(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( PINISPOOLER pIniSpooler, DWORD Message, WPARAM wParam, LPARAM lParam ) { HANDLE hThread; DWORD ThreadId; PMESSAGE pMessage; if (( pIniSpooler != NULL ) && ( pIniSpooler->SpoolerFlags & SPL_BROADCAST_CHANGE )) { // debug BroadcastCount++; // end debug if ((pMessage = AllocSplMem(sizeof(MESSAGE))) == NULL){ // // if the AllocSplMem fails, then bomb out, cannot send message // return; } pMessage->Message = Message; pMessage->wParam = wParam; // Clone the source string // The caller of BroadcastChange might free the string // before we the other thread has done the SendMessage. if ( Message == WM_DEVMODECHANGE ) { pMessage->lParam = ( LPARAM)AllocSplStr( (LPWSTR)lParam ); } else { pMessage->lParam = lParam; } // BUGBUG mattfe Nov 8 93 // 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, 4096, (LPTHREAD_START_ROUTINE)SendMessageThread, (LPVOID)pMessage, 0, &ThreadId ); CloseHandle(hThread); } else { DBGMSG(DBG_TRACE, ("BroadCastChange Ignoring Change\n")); } } VOID MessageBeepThread( DWORD fuType ) { MessageBeep(fuType); ExitThread(0); } VOID MyMessageBeep( DWORD fuType ) { HANDLE hThread; DWORD ThreadId; if ( dwBeepEnabled != 0 ) { hThread = CreateThread( NULL, 4096, (LPTHREAD_START_ROUTINE)MessageBeepThread, (LPVOID)fuType, 0, &ThreadId ); CloseHandle(hThread); } } // Recursively delete any subkeys of a given key. // Assumes that RevertToPrinterSelf() has been called. DWORD DeleteSubkeys( HKEY hKey ) { DWORD cbData, cSubkeys; WCHAR SubkeyName[MAX_PATH]; HKEY hSubkey; LONG Status; cSubkeys = 0; cbData = sizeof(SubkeyName); while ((Status = RegEnumKeyEx(hKey, cSubkeys, SubkeyName, &cbData, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS) { Status = RegOpenKeyEx(hKey, SubkeyName, 0, KEY_READ | KEY_WRITE, &hSubkey); if( Status == ERROR_SUCCESS ) { Status = DeleteSubkeys(hSubkey); RegCloseKey(hSubkey); if( Status == ERROR_SUCCESS ) RegDeleteKey(hKey, SubkeyName); } // cSubkeys++; Oops: We've deleted the 0th subkey, so the next one is 0 too! cbData = sizeof(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 } BOOL ValidateSpoolHandle( PSPOOL pSpool, DWORD dwDisallowMask ) { BOOL ReturnValue; try { if (( pSpool->signature != SJ_SIGNATURE ) || ( pSpool->TypeofHandle & dwDisallowMask ) || ( 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 ) /*++ 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. Return Value: TRUE - Success FALSE - failed something major, like allocating memory. --*/ { BOOL bReturnValue = FALSE; HANDLE hFindFile; WIN32_FIND_DATA FindData; WCHAR ScratchBuffer[ MAX_PATH ]; SPLASSERT( pDirectory != NULL ); wsprintf( ScratchBuffer, L"%ws\\*", pDirectory ); 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 // wsprintf( ScratchBuffer, L"%ws\\%ws", pDirectory, FindData.cFileName ); if ( !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 ) /*++ 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; hToken = RevertToPrinterSelf(); bReturnValue = DeleteAllFilesInDirectory( pDirectory ); if ( !RemoveDirectory( pDirectory )) { if (!MoveFileEx( 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 deleted %ws OK\n", pDirectory )); } if ( hToken != INVALID_HANDLE_VALUE ) { ImpersonatePrinterClient(hToken); } return bReturnValue; } // Returns a pointer to a copy of the source string with backslashes removed. // This is to store the printer name as the key name in the registry, // which interprets backslashes as branches in the registry structure. // Convert them to commas, since we don't allow printer names with commas, // so there shouldn't be any clashes. // If there are no backslashes, the string is unchanged. LPWSTR RemoveBackslashesForRegistryKey( LPWSTR pSource, // The string from which backslashes are to be removed. const LPWSTR pScratch // Scratch buffer for the function to write in, must be at least as long as pSource. ) { if ( pSource != NULL ) { // Copy the string into the scratch buffer: wcscpy (pScratch, pSource); // Check each character, and, if it's a backslash, // convert it to an comma for (pSource = pScratch; *pSource; pSource++) { if (*pSource == L'\\') *pSource = *szComma; } } else { *pScratch = 0; } return pScratch; } // // 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 SameDependentFiles( LPWSTR DependentFiles1, LPWSTR DependentFiles2 ) { LPWSTR FileName1, FileName2; if ( !DependentFiles1 && !DependentFiles2 ) return TRUE; if ( !DependentFiles1 || !DependentFiles2 ) return FALSE; // Check there are same number of strings (filenames) for ( FileName1 = DependentFiles1, FileName2 = DependentFiles2 ; *FileName1 && *FileName2 ; FileName1 += wcslen(FileName1)+1, FileName2 += wcslen(FileName2)+1 ) ; // Different number of filenames if ( *FileName1 || *FileName2 ) return FALSE; // check in DependentFiles2 for each FileName in DependentFiles1 for ( FileName1 = DependentFiles1 ; *FileName1 ; FileName1 += wcslen(FileName1) + 1 ) { for ( FileName2 = DependentFiles2 ; *FileName2 && _wcsicmp(FileName1, FileName2) ; FileName2 += wcslen(FileName2) + 1 ) { } // We did not find the filename if ( ! *FileName2 ) { return FALSE; } } return TRUE; } // // Wcsicmp requires non-NULL pointers, this is an extended version // handling NULL pointers withour throwing an exception // We will treat a pointer to NULL same as a NULL pointer // int wcsicmpEx( LPWSTR s1, LPWSTR s2 ) { if ( s1 && *s1 ) { if ( s2 && *s2 ) { return _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 ) { 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 = RegSetValueEx(hKey, pValueName, 0, REG_SZ, (LPBYTE)pString, cbString ); 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 ) { BOOL bReturnValue; LPWSTR pString; DWORD Status; Status = RegSetValueEx(hKey, pValueName, 0, REG_DWORD, (LPBYTE)&dwParam, sizeof(DWORD) ); 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 ) { DWORD Status; BOOL bReturnValue; Status = RegSetValueEx( hKey, pValueName, 0, REG_BINARY, pData, cbData ); 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 ) { 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 = RegSetValueEx(hKey, pValueName, 0, REG_MULTI_SZ, (LPBYTE)pString, cchString ); 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 ) /*++ 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 = RegQueryValueEx(hKey, pValueName, 0, &Type, NULL, &cbValue ); 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 = RegQueryValueEx(hKey, pValueName, 0, &Type, (LPBYTE) *ppValue, &cbValue); 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; } 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])); } } } BOOL AllocOrUpdateString( LPWSTR *ppString, LPWSTR pNewValue, LPWSTR pOldValue, BOOL *bFail) /*++ 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 bFail: On error set this to TRUE (Note: it could already be TRUE) Return Value: TRUE on success, else FALSE History: MuhuntS -- Aug 95 --*/ { BOOL bReturn = TRUE; if ( *bFail ) return FALSE; if ( wcsicmpEx(pNewValue, pOldValue) ) { if ( pNewValue && *pNewValue ) { if ( !(*ppString = AllocSplStr(pNewValue)) ) { *bFail = TRUE; bReturn = FALSE; } } else { *ppString = NULL; } } return bReturn; } 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]); } } } 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) --*/ { PINIVERSION pIniVersion = NULL; PINIENVIRONMENT pIniEnvironment; PINIDRIVER pIniDriver; PINISPOOLER pIniSpooler = pIniPrinter->pIniSpooler; DWORD dwIndex; WCHAR szDriverPath[MAX_PATH]; // // Find driver file for the given driver and then get it's fullpath // SPLASSERT(pIniPrinter && pIniPrinter->pIniDriver && pIniPrinter->pIniDriver->pName); pIniEnvironment = FindEnvironment(szEnvironment); pIniDriver = FindCompatibleDriver(pIniEnvironment, &pIniVersion, pIniPrinter->pIniDriver->pName, cThisMajorVersion, FIND_COMPATIBLE_VERSION); if ( !pIniDriver ) { SPLASSERT(pIniDriver != NULL); return NULL; } dwIndex = GetDriverVersionDirectory(szDriverPath, pIniEnvironment, pIniVersion, FALSE, pIniPrinter->pIniSpooler); szDriverPath[dwIndex++] = L'\\'; 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, pszLocalPrinterNameWithToken = pszPrinterNameWithToken; LPDEVMODE pNewDevMode = NULL, pOldDevMode = NULL; DWORD dwNeeded, dwLastError; HANDLE hDevModeConvert = NULL; SplInSem(); // // If ConfigFile or PrinterNameWithToken is not given allocate it locally // if ( !pszLocalConfigFile ) { pszLocalConfigFile = GetConfigFilePath(pIniPrinter); if ( !pszLocalConfigFile ) goto Cleanup; } if ( !pszLocalPrinterNameWithToken ) { dwNeeded = wcslen(pIniPrinter->pName) + wcslen(pszUpgradeToken) + 2; dwNeeded *= sizeof(WCHAR); pszLocalPrinterNameWithToken = (LPWSTR) AllocSplMem(dwNeeded); if ( !pszLocalPrinterNameWithToken ) goto Cleanup; wsprintf(pszLocalPrinterNameWithToken, L"%ws,%ws", pIniPrinter->pName, pszUpgradeToken); } // // 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 ) 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); 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 ) { FreeSplMem(pNewDevMode); pNewDevMode = NULL; goto CleanupFromOutsideSplSem; } } } CleanupFromOutsideSplSem: 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':'; } DWORD UnicodeToAnsiString( LPWSTR pUnicode, LPSTR pAnsi, DWORD dwBufferSize ) /*++ Description: Convert UNICODE string to ANSI. Routine allocates memory from the heap which should be freed by the caller. Arguments: pUnicode - Points to a NULL-terminated UNICODE string pAnsi - Points to buffer where ANSI string should be built dwBufferSize - Size of pAnsi buffer Return Vlaue: Pointer to ANSI string --*/ { return WideCharToMultiByte(CP_ACP, 0, pUnicode, -1, pAnsi, dwBufferSize, NULL, NULL); } 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 = FindMonitorOnLocalSpooler(pMonitorName) != 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 = AllocSplStr( pIniPrinter->pName ); 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_WARNING, ("SplDriverEvent %ws %d %x\n", pName, PrinterEvent, lParam)); ReturnValue = (*pfnPrinterEvent)( pName, PrinterEvent, PRINTER_EVENT_FLAG_NO_UI, lParam ); } return ReturnValue; }