/************************************************************************ Copyright (c) 2002 Microsoft Corporation Module Name : utils.cpp Abstract : Utility functions for BITSADMIN Author : Mike Zoran mzoran May 2002. Revision History : Notes: ***********************************************************************/ #include "bitsadmin.h" // // Globals // bool g_Shutdown = false; HANDLE g_MainThreadHandle = NULL; WCHAR* pComputerName; SmartManagerPointer g_Manager; bool bRawReturn = false; bool bWrap = true; bool bExplicitWrap = false; bool bConsoleInfoRetrieved = false; HANDLE hConsole; CRITICAL_SECTION CritSection; CONSOLE_SCREEN_BUFFER_INFO StartConsoleInfo; DWORD StartConsoleMode; BITSOUTStream bcout( STD_OUTPUT_HANDLE ); BITSOUTStream bcerr( STD_ERROR_HANDLE ); void BITSADMINSetThreadUILanguage() { LANGID LangId; switch (GetConsoleOutputCP()) { case 932: LangId = MAKELANGID( LANG_JAPANESE, SUBLANG_DEFAULT ); break; case 949: LangId = MAKELANGID( LANG_KOREAN, SUBLANG_KOREAN ); break; case 936: LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED ); break; case 950: LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL ); break; default: LangId = PRIMARYLANGID(LANGIDFROMLCID( GetUserDefaultLCID() )); if (LangId == LANG_JAPANESE || LangId == LANG_KOREAN || LangId == LANG_CHINESE ) { LangId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ); } else { LangId = MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ); } break; } SetThreadLocale( MAKELCID(LangId, SORT_DEFAULT) ); return; } void BITSOUTStream::OutputString( const WCHAR *RawString ) { SIZE_T CurrentPos = 0; PollShutdown(); if ( !RawString ) RawString = L"NULL"; while( RawString[ CurrentPos ] != '\0' ) { if ( L'\n' == RawString[ CurrentPos ] ) { Buffer[ BufferUsed++ ] = L'\x000D'; Buffer[ BufferUsed++ ] = L'\x000A'; CurrentPos++; FlushBuffer( true ); } else if ( L'\t' == RawString[ CurrentPos ] ) { // Tabs complicate things, flush them FlushBuffer(); Buffer[ BufferUsed++ ] = RawString[ CurrentPos++ ]; FlushBuffer(); } else { Buffer[ BufferUsed++ ] = RawString[ CurrentPos++ ]; if ( BufferUsed >= ( 4096 - 10 ) ) // keep a pad of 10 chars FlushBuffer(); } } } void BITSOUTStream::FlushBuffer( bool HasNewLine ) { if (!BufferUsed) return; if( GetFileType(Handle) == FILE_TYPE_CHAR ) { DWORD CharsWritten; if ( bWrap ) WriteConsoleW( Handle, Buffer, BufferUsed, &CharsWritten, 0); else { // The console code has what appears to be a bug in that it doesn't // handle the case were line wrapping is disabled and WriteConsoleW // is called. Need to manually handle the truncation. CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo; GetConsoleScreenBufferInfo( Handle, &ConsoleScreenBufferInfo ); SHORT Columns = ( ConsoleScreenBufferInfo.dwSize.X - 1 ) - ( ConsoleScreenBufferInfo.dwCursorPosition.X ); DWORD ActualChars = HasNewLine ? ( BufferUsed - 2 ) : BufferUsed; if ( Columns >= (INT32)ActualChars ) WriteConsoleW( Handle, Buffer, BufferUsed, &CharsWritten, 0 ); else { WriteConsoleW( Handle, Buffer, Columns, &CharsWritten, 0 ); if ( HasNewLine ) WriteConsoleW( Handle, Buffer + ActualChars, 2, &CharsWritten, 0 ); } } } else { int ByteCount = WideCharToMultiByte( GetConsoleOutputCP(), 0, Buffer, BufferUsed, MBBuffer, sizeof(MBBuffer), 0, 0); // SEC-REVIEWED: 2002-03-21 if ( ByteCount ) { if ( MBBuffer[ByteCount-1] == '\0' ) ByteCount--; DWORD BytesWritten; while( ByteCount ) { if ( !WriteFile(Handle, MBBuffer, ByteCount, &BytesWritten, 0) ) // SEC-REVIEWED: 2002-03-21 CheckHR( L"Unable to write to the output file", HRESULT_FROM_WIN32( GetLastError() ) ); ByteCount -= BytesWritten; } } } BufferUsed = 0; } BITSOUTStream& operator<< (BITSOUTStream &s, const WCHAR * String ) { s.OutputString( String ); return s; } BITSOUTStream& operator<< (BITSOUTStream &s, UINT64 Number ) { static WCHAR Buffer[256]; if ( FAILED( StringCbPrintf( Buffer, sizeof(Buffer), L"%I64u", Number ) ) ) return s; return ( s << Buffer ); } WCHAR * HRESULTToString( HRESULT Hr ) { static WCHAR ErrorCode[12]; if ( FAILED( StringCbPrintf( ErrorCode, sizeof(ErrorCode), L"0x%8.8x", Hr ) ) ) { ErrorCode[0] = '\0'; } return ErrorCode; } BITSOUTStream& operator<< ( BITSOUTStream &s, AutoStringPointer & String ) { return ( s << String.Get() ); } BITSOUTStream& operator<< ( BITSOUTStream &s, GUID & guid ) { WCHAR GUIDSTR[40]; if (!StringFromGUID2( guid, GUIDSTR, 40 )) { bcout << L"Internal error converting guid to string.\n"; throw AbortException(1); } return ( s << GUIDSTR ); } BITSOUTStream& operator<< ( BITSOUTStream &s, FILETIME & filetime ) { // Convert the time and date into a localized string. // If an error occures, ignore it and print ERROR instead if ( !filetime.dwLowDateTime && !filetime.dwHighDateTime ) return ( s << L"UNKNOWN" ); FILETIME localtime; FileTimeToLocalFileTime( &filetime, &localtime ); SYSTEMTIME systemtime; FileTimeToSystemTime( &localtime, &systemtime ); // Get the required date size int RequiredDateSize = GetDateFormatW( LOCALE_USER_DEFAULT, 0, &systemtime, NULL, NULL, 0 ); if (!RequiredDateSize) return ( s << L"ERROR" ); CAutoString DateBuffer( new WCHAR[ RequiredDateSize + 1 ]); // Actually retrieve the date int DateSize = GetDateFormatW( LOCALE_USER_DEFAULT, 0, &systemtime, NULL, DateBuffer.get(), RequiredDateSize ); if (!DateSize) return ( s << L"ERROR" ); // Get the required time size int RequiredTimeSize = GetTimeFormatW( LOCALE_USER_DEFAULT, 0, &systemtime, NULL, NULL, 0 ); if (!RequiredTimeSize) return ( s << L"ERROR" ); CAutoString TimeBuffer( new WCHAR[ RequiredTimeSize + 1 ]); int TimeSize = GetTimeFormatW( LOCALE_USER_DEFAULT, 0, &systemtime, NULL, TimeBuffer.get(), RequiredTimeSize ); if (!TimeSize) return ( s << L"ERROR" ); return ( s << DateBuffer.get() << L" " << TimeBuffer.get() ); } BITSOUTStream& operator<< ( BITSOUTStream &s, BG_JOB_PROXY_USAGE ProxyUsage ) { switch( ProxyUsage ) { case BG_JOB_PROXY_USAGE_PRECONFIG: return (s << L"PRECONFIG"); case BG_JOB_PROXY_USAGE_NO_PROXY: return (s << L"NO_PROXY"); case BG_JOB_PROXY_USAGE_OVERRIDE: return (s << L"OVERRIDE"); default: return (s << L"UNKNOWN"); } } ULONG InputULONG( WCHAR *pText ) { ULONG number; if ( 1 != swscanf( pText, L"%u", &number ) ) { bcout << L"Invalid number.\n"; throw AbortException(1); } return number; } BOOL LocalConvertStringSidToSid ( IN PWSTR StringSid, OUT PSID *Sid, OUT PWSTR *End ) /*++ Routine Description: This routine will convert a string representation of a SID back into a sid. The expected format of the string is: "S-1-5-32-549" If a string in a different format or an incorrect or incomplete string is given, the operation is failed. The returned sid must be free via a call to LocalFree Arguments: StringSid - The string to be converted Sid - Where the created SID is to be returned End - Where in the string we stopped processing Return Value: TRUE - Success. FALSE - Failure. Additional information returned from GetLastError(). Errors set are: ERROR_SUCCESS indicates success ERROR_NOT_ENOUGH_MEMORY indicates a memory allocation for the ouput sid failed ERROR_INVALID_SID indicates that the given string did not represent a sid --*/ { DWORD Err = ERROR_SUCCESS; UCHAR Revision, Subs; SID_IDENTIFIER_AUTHORITY IDAuth; PULONG SubAuth = NULL; PWSTR CurrEnd, Curr, Next; WCHAR Stub, *StubPtr = NULL; ULONG Index; INT gBase=10; INT lBase=10; ULONG Auto; if ( NULL == StringSid || NULL == Sid || NULL == End ) { SetLastError( ERROR_INVALID_PARAMETER ); return( FALSE ); } // // no need to check length because StringSid is NULL // and if the first char is NULL, it won't access the second char // if ( (*StringSid != L'S' && *StringSid != L's') || *( StringSid + 1 ) != L'-' ) { // // string sid should always start with S- // SetLastError( ERROR_INVALID_SID ); return( FALSE ); } Curr = StringSid + 2; if ( (*Curr == L'0') && ( *(Curr+1) == L'x' || *(Curr+1) == L'X' ) ) { gBase = 16; } Revision = ( UCHAR )wcstol( Curr, &CurrEnd, gBase ); if ( CurrEnd == Curr || *CurrEnd != L'-' || *(CurrEnd+1) == L'\0' ) { // // no revision is provided, or invalid delimeter // SetLastError( ERROR_INVALID_SID ); return( FALSE ); } Curr = CurrEnd + 1; // // Count the number of characters in the indentifer authority... // Next = wcschr( Curr, L'-' ); if ( (*Curr == L'0') && ( *(Curr+1) == L'x' || *(Curr+1) == L'X' ) ) { lBase = 16; } else { lBase = gBase; } Auto = wcstoul( Curr, &CurrEnd, lBase ); if ( CurrEnd == Curr || *CurrEnd != L'-' || *(CurrEnd+1) == L'\0' ) { // // no revision is provided, or invalid delimeter // SetLastError( ERROR_INVALID_SID ); return( FALSE ); } IDAuth.Value[0] = IDAuth.Value[1] = 0; IDAuth.Value[5] = ( UCHAR )Auto & 0xFF; IDAuth.Value[4] = ( UCHAR )(( Auto >> 8 ) & 0xFF ); IDAuth.Value[3] = ( UCHAR )(( Auto >> 16 ) & 0xFF ); IDAuth.Value[2] = ( UCHAR )(( Auto >> 24 ) & 0xFF ); Curr = CurrEnd; // // Now, count the number of sub auths, at least one sub auth is required // Subs = 0; Next = Curr; // // We'll have to count our sub authoritys one character at a time, // since there are several deliminators that we can have... // while ( Next ) { if ( *Next == L'-' && *(Next-1) != L'-') { // // do not allow two continuous '-'s // We've found one! // Subs++; if ( (*(Next+1) == L'0') && ( *(Next+2) == L'x' || *(Next+2) == L'X' ) ) { // // this is hex indicator // Next += 2; } } else if ( *Next == SDDL_SEPERATORC || *Next == L'\0' || *Next == SDDL_ACE_ENDC || *Next == L' ' || ( *(Next+1) == SDDL_DELIMINATORC && (*Next == L'G' || *Next == L'O' || *Next == L'S')) ) { // // space is a terminator too // if ( *( Next - 1 ) == L'-' ) { // // shouldn't allow a SID terminated with '-' // Err = ERROR_INVALID_SID; Next--; } else { Subs++; } *End = Next; break; } else if ( !iswxdigit( *Next ) ) { Err = ERROR_INVALID_SID; *End = Next; break; } else { // // Note: SID is also used as a owner or group // // Some of the tags (namely 'D' for Dacl) fall under the category of iswxdigit, so // if the current character is a character we care about and the next one is a // delminiator, we'll quit // if ( *Next == L'D' && *( Next + 1 ) == SDDL_DELIMINATORC ) { // // We'll also need to temporarily truncate the string to this length so // we don't accidentally include the character in one of the conversions // Stub = *Next; StubPtr = Next; *StubPtr = UNICODE_NULL; *End = Next; Subs++; break; } } Next++; } if ( Err == ERROR_SUCCESS ) { if ( Subs != 0 ) Subs--; if ( Subs != 0 ) { Curr++; SubAuth = ( PULONG )LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, Subs * sizeof( ULONG ) ); if ( SubAuth == NULL ) { Err = ERROR_NOT_ENOUGH_MEMORY; } else { for ( Index = 0; Index < Subs; Index++ ) { if ( (*Curr == L'0') && ( *(Curr+1) == L'x' || *(Curr+1) == L'X' ) ) { lBase = 16; } else { lBase = gBase; } SubAuth[Index] = wcstoul( Curr, &CurrEnd, lBase ); Curr = CurrEnd + 1; } } } else { Err = ERROR_INVALID_SID; } } // // Now, create the SID // if ( Err == ERROR_SUCCESS ) { *Sid = ( PSID )LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, sizeof( SID ) + Subs * sizeof( ULONG ) ); if ( *Sid == NULL ) { Err = ERROR_NOT_ENOUGH_MEMORY; } else { PISID ISid = ( PISID )*Sid; ISid->Revision = Revision; ISid->SubAuthorityCount = Subs; ISid->IdentifierAuthority = IDAuth; RtlCopyMemory( ISid->SubAuthority, SubAuth, Subs * sizeof( ULONG ) ); // SEC-REVIEWED: 2002-03-21 } } LocalFree( SubAuth ); // // Restore any character we may have stubbed out // if ( StubPtr ) { *StubPtr = Stub; } SetLastError( Err ); return( Err == ERROR_SUCCESS ); } BOOL AltConvertStringSidToSid( IN LPCWSTR StringSid, OUT PSID *Sid ) /*++ Routine Description: This routine converts a stringized SID into a valid, functional SID Arguments: StringSid - SID to be converted. Sid - Where the converted SID is returned. Buffer is allocated via LocalAlloc and should be free via LocalFree. Return Value: TRUE - Success FALSE - Failure Extended error status is available using GetLastError. ERROR_INVALID_PARAMETER - A NULL name was given ERROR_INVALID_SID - The format of the given sid was incorrect --*/ { PWSTR End = NULL; BOOL ReturnValue = FALSE; PSID pSASid=NULL; ULONG Len=0; DWORD SaveCode=0; DWORD Err=0; if ( StringSid == NULL || Sid == NULL ) { SetLastError( ERROR_INVALID_PARAMETER ); return ReturnValue; } ReturnValue = LocalConvertStringSidToSid( ( PWSTR )StringSid, Sid, &End ); if ( !ReturnValue ) { SetLastError( ERROR_INVALID_PARAMETER ); return ReturnValue; } if ( ( ULONG )( End - StringSid ) != wcslen( StringSid ) ) { SetLastError( ERROR_INVALID_SID ); LocalFree( *Sid ); *Sid = FALSE; ReturnValue = FALSE; } else { SetLastError(ERROR_SUCCESS); } return ReturnValue; } BITSOUTStream& operator<< ( BITSOUTStream &s, PrintSidString SidString ) { // Convert the SID string into the user name // in domain\account format. // If an error occures, just return the SID string PSID pSid = NULL; BOOL bResult = AltConvertStringSidToSid( SidString.m_SidString, &pSid ); if ( !bResult ) { return ( s << SidString.m_SidString ); } SID_NAME_USE NameUse; DWORD dwNameSize = 0; DWORD dwDomainSize = 0; bResult = LookupAccountSid( NULL, pSid, NULL, &dwNameSize, NULL, &dwDomainSize, &NameUse); if ( bResult || ( GetLastError() != ERROR_INSUFFICIENT_BUFFER ) ) { LocalFree( pSid ); return ( s << SidString.m_SidString ); } CAutoString pName( new WCHAR[ dwNameSize ] ); CAutoString pDomain( new WCHAR[ dwDomainSize ] ); bResult = LookupAccountSid( NULL, pSid, pName.get(), &dwNameSize, pDomain.get(), &dwDomainSize, &NameUse); if (!bResult) { LocalFree( pSid ); return ( s << SidString.m_SidString ); } LocalFree( pSid ); return ( s << pDomain.get() << L"\\" << pName.get() ); } void * _cdecl operator new( size_t Size ) { void *Memory = CoTaskMemAlloc( Size ); if ( !Memory ) { bcout << L"Out of memory while allocating " << Size << L" bytes.\n"; throw AbortException( (int)E_OUTOFMEMORY ); } return Memory; } void _cdecl operator delete( void *Mem ) { CoTaskMemFree( Mem ); } void PollShutdown() { if ( g_Shutdown ) throw AbortException( (DWORD)CONTROL_C_EXIT ); } void ShutdownAPC( ULONG_PTR ) { return; } void SignalShutdown( DWORD MilliTimeout ) { g_Shutdown = true; // Queue a shutdown APC if ( g_MainThreadHandle ) { QueueUserAPC( ShutdownAPC, g_MainThreadHandle, NULL ); } Sleep( MilliTimeout ); RestoreConsole(); TerminateProcess( GetCurrentProcess(), (DWORD)CONTROL_C_EXIT ); } void CheckHR( const WCHAR *pFailTxt, HRESULT Hr ) { // Check error code for success, and exit // with a failure message if unsuccessfull. if ( !SUCCEEDED(Hr) ) { WCHAR ErrorCode[12]; if ( SUCCEEDED( StringCbPrintf( ErrorCode, sizeof(ErrorCode), L"0x%8.8x", Hr ) ) ) { bcout << pFailTxt << L" - " << ErrorCode << L"\n"; WCHAR *pMessage = NULL; if ( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, (DWORD)Hr, GetThreadLocale(), (WCHAR*)&pMessage, 0, NULL ) ) { bcout << pMessage << L"\n"; LocalFree( pMessage ); } } throw AbortException( Hr ); } } void SetupConsole() { if (!( GetFileType( bcout.GetHandle() ) == FILE_TYPE_CHAR ) ) return; hConsole = bcout.GetHandle(); if ( INVALID_HANDLE_VALUE == hConsole ) CheckHR( L"Unable to get console handle", HRESULT_FROM_WIN32( GetLastError() ) ); if (!GetConsoleScreenBufferInfo( hConsole, &StartConsoleInfo ) ) CheckHR( L"Unable get setup console information", HRESULT_FROM_WIN32( GetLastError() ) ); if (!GetConsoleMode( hConsole, &StartConsoleMode ) ) CheckHR( L"Unable get setup console information", HRESULT_FROM_WIN32( GetLastError() ) ); InitializeCriticalSection( &CritSection ); bConsoleInfoRetrieved = true; EnterCriticalSection( &CritSection ); DWORD NewConsoleMode = ( bWrap ) ? ( StartConsoleMode | ENABLE_WRAP_AT_EOL_OUTPUT ) : ( StartConsoleMode & ~ENABLE_WRAP_AT_EOL_OUTPUT ); if (!SetConsoleMode( hConsole, NewConsoleMode ) ) CheckHR( L"Unable set console mode", HRESULT_FROM_WIN32( GetLastError() ) ); LeaveCriticalSection( &CritSection ); } void ChangeConsoleMode() { EnterCriticalSection( &CritSection ); DWORD NewConsoleMode = ( bWrap ) ? ( StartConsoleMode | ENABLE_WRAP_AT_EOL_OUTPUT ) : ( StartConsoleMode & ~ENABLE_WRAP_AT_EOL_OUTPUT ); if (!SetConsoleMode( hConsole, NewConsoleMode ) ) CheckHR( L"Unable set console mode", HRESULT_FROM_WIN32( GetLastError() ) ); LeaveCriticalSection( &CritSection ); } void RestoreConsole() { if ( bConsoleInfoRetrieved ) { EnterCriticalSection( &CritSection ); SetConsoleTextAttribute( hConsole, StartConsoleInfo.wAttributes ); SetConsoleMode( hConsole, StartConsoleMode ); // Do not unlock, since we shouldn't allow any more console attribute changes } } void ClearScreen() { COORD coordScreen = { 0, 0 }; BOOL bSuccess; DWORD cCharsWritten; CONSOLE_SCREEN_BUFFER_INFO csbi; DWORD dwConSize; EnterCriticalSection( &CritSection ); if (!GetConsoleScreenBufferInfo(hConsole, &csbi)) goto error; dwConSize = csbi.dwSize.X * csbi.dwSize.Y; if (!FillConsoleOutputCharacter(hConsole, (WCHAR) ' ', dwConSize, coordScreen, &cCharsWritten)) goto error; if (!GetConsoleScreenBufferInfo(hConsole, &csbi)) goto error; if (!FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten)) goto error; if (!SetConsoleCursorPosition(hConsole, coordScreen)) goto error; LeaveCriticalSection( &CritSection ); return; error: DWORD dwError = GetLastError(); LeaveCriticalSection( &CritSection ); CheckHR( L"Unable to clear the console window", HRESULT_FROM_WIN32( dwError ) ); throw AbortException( dwError ); } BITSOUTStream & operator<<( BITSOUTStream & s, AddIntensity ) { if ( GetFileType( s.GetHandle() ) == FILE_TYPE_CHAR ) { s.FlushBuffer(); EnterCriticalSection( &CritSection ); SetConsoleTextAttribute( hConsole, StartConsoleInfo.wAttributes | FOREGROUND_INTENSITY ); LeaveCriticalSection( &CritSection ); } return s; } BITSOUTStream & operator<<( BITSOUTStream & s, ResetIntensity ) { if ( GetFileType( s.GetHandle() ) == FILE_TYPE_CHAR ) { s.FlushBuffer(); EnterCriticalSection( &CritSection ); SetConsoleTextAttribute( hConsole, StartConsoleInfo.wAttributes ); LeaveCriticalSection( &CritSection ); } return s; }