xref: /trunk/main/desktop/win32/source/setup/setup.cpp (revision 908cc4a9)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #define WIN // scope W32 API
25 
26 #if defined _MSC_VER
27 #pragma warning(push, 1)
28 #endif
29 #include <windows.h>
30 #if defined _MSC_VER
31 #pragma warning(pop)
32 #endif
33 #include <tchar.h>
34 #include <assert.h>
35 #include <shlwapi.h>
36 #include <new>
37 #include <time.h>
38 #include <mbctype.h>
39 #include <locale.h>
40 #include <Msiquery.h>
41 #include <MsiDefs.h>
42 #include "strsafe.h"
43 
44 #include "setup.hxx"
45 
46 #include "resource.h"
47 
48 //--------------------------------------------------------------------------
49 
50 #define MAX_STR_LENGTH     32000
51 #define MAX_TEXT_LENGTH     1024
52 #define MAX_LANGUAGE_LEN      80
53 #define MAX_STR_CAPTION      256
54 #define VERSION_SIZE          80
55 #define SECTION_SETUP       TEXT( "Setup" )
56 #define SECTION_LANGUAGE    TEXT( "Languages" )
57 #define PRODUCT_NAME_VAR    TEXT( "%PRODUCTNAME" )
58 #define PRODUCT_VERSION     TEXT( "ProductVersion" )
59 #define ERROR_SHOW_USAGE      -2
60 #define ERROR_SETUP_TO_OLD    -3
61 #define ERROR_SETUP_NOT_FOUND -4
62 
63 #define PARAM_SETUP_USED    TEXT( " SETUP_USED=1 " )
64 #define PARAM_PACKAGE       TEXT( "/I " )
65 #define PARAM_MINOR_UPGRADE TEXT( "/FVOMUS " )
66 #define PARAM_ADMIN         TEXT( "/A " )
67 #define PARAM_TRANSFORM     TEXT( " TRANSFORMS=" )
68 #define PARAM_REBOOT        TEXT( " REBOOT=Force" )
69 #define PARAM_PATCH         TEXT( " /update " )
70 #define PARAM_REG_ALL_MSO_TYPES TEXT( "REGISTER_ALL_MSO_TYPES=1 " )
71 #define PARAM_REG_NO_MSO_TYPES  TEXT( "REGISTER_NO_MSO_TYPES=1 " )
72 #define PARAM_SILENTINSTALL     TEXT( " /Q" )
73 
74 #define PARAM_RUNNING           TEXT( "ignore_running" )
75 #define CMDLN_REG_ALL_MSO_TYPES TEXT( "msoreg=1" )
76 #define CMDLN_REG_NO_MSO_TYPES  TEXT( "msoreg=0" )
77 
78 #define MSI_DLL             TEXT( "msi.dll" )
79 #define ADVAPI32_DLL        TEXT( "advapi32.dll" )
80 #define PROFILE_NAME        TEXT( "setup.ini" )
81 
82 #define RUNTIME_X64_NAME    TEXT( "redist\\vcredist_x64.exe" )
83 #define RUNTIME_X86_NAME    TEXT( "redist\\vcredist_x86.exe" )
84 // Microsoft Visual C++ 2008 Redistributable - x86 9.0.30729.6161
85 #define PRODUCTCODE_X86     TEXT( "{9BE518E6-ECC6-35A9-88E4-87755C07200F}" )
86 // Microsoft Visual C++ 2008 Redistributable - x64 9.0.30729.6161
87 #define PRODUCTCODE_X64     TEXT( "{5FCE6D76-F5DC-37AB-B2B8-22AB8CEDB1D4}" )
88 
89 #define MSIAPI_DllGetVersion     "DllGetVersion"
90 #define ADVAPI32API_CheckTokenMembership "CheckTokenMembership"
91 
92 typedef HRESULT (CALLBACK* PFnDllGetVersion)( DLLVERSIONINFO *pdvi);
93 typedef BOOL (WINAPI* PFnCheckTokenMembership)(HANDLE TokenHandle, PSID SidToCheck, PBOOL IsMember);
94 
95 #ifdef DEBUG
OutputDebugStringFormat(LPCTSTR pFormat,...)96 inline void OutputDebugStringFormat( LPCTSTR pFormat, ... )
97 {
98 	TCHAR    buffer[1024];
99 	va_list  args;
100 
101 	va_start( args, pFormat );
102 	StringCchVPrintf( buffer, sizeof(buffer), pFormat, args );
103 	OutputDebugString( buffer );
104 }
105 #else
OutputDebugStringFormat(LPCTSTR,...)106 static inline void OutputDebugStringFormat( LPCTSTR, ... )
107 {
108 }
109 #endif
110 
111 //--------------------------------------------------------------------------
112 
113 const TCHAR sInstKey[]       = TEXT( "Software\\Microsoft\\Windows\\CurrentVersion\\Installer" );
114 const TCHAR sInstLocValue[]  = TEXT( "InstallerLocation" );
115 const TCHAR sMsiDll[]        = TEXT( "\\msi.dll" );
116 const TCHAR sMsiExe[]        = TEXT( "\\msiexec.exe" );
117 const TCHAR sDelayReboot[]   = TEXT( " /c:\"msiinst /delayreboot\"" );
118 const TCHAR sMsiQuiet[]      = TEXT( " /q" );
119 const TCHAR sMemMapName[]    = TEXT( "Global\\MsiErrorObject" );
120 
121 //--------------------------------------------------------------------------
SetupAppX()122 SetupAppX::SetupAppX()
123 {
124     m_hInst     = NULL;
125     m_hMapFile  = NULL;
126     m_pAppTitle = NULL;
127     m_pCmdLine  = NULL;
128 
129     m_pDatabase = NULL;
130     m_pReqVersion   = NULL;
131     m_pProductName  = NULL;
132     m_pAdvertise    = NULL;
133     m_pTmpName      = NULL;
134     m_pLogFile      = NULL;
135     m_pModuleFile   = NULL;
136     m_pPatchFiles   = NULL;
137     m_pMSIErrorCode = NULL;
138     m_pUpgradeKey   = NULL;
139     m_pProductVersion = NULL;
140 
141     m_pErrorText    = new TCHAR[ MAX_TEXT_LENGTH ];
142     m_pErrorText[0] = '\0';
143 
144     m_nLanguageID     = 0;
145     m_nLanguageCount  = 0;
146     m_ppLanguageList  = NULL;
147 
148     m_bQuiet          = false;
149     m_bRegNoMsoTypes  = false;
150     m_bRegAllMsoTypes = false;
151     m_bIsMinorUpgrade = false;
152     m_bSupportsPatch  = false;
153 
154     m_bIgnoreAlreadyRunning = false;
155 }
156 
157 //--------------------------------------------------------------------------
~SetupAppX()158 SetupAppX::~SetupAppX()
159 {
160     if ( m_ppLanguageList )
161     {
162         for ( int i = 0; i < m_nLanguageCount; i++ )
163             if ( m_ppLanguageList[i] )
164                 delete m_ppLanguageList[ i ];
165         delete [] m_ppLanguageList;
166     }
167 
168     time_t aTime;
169     time( &aTime );
170     tm *pTime = localtime( &aTime );   // Convert time to struct tm form
171 
172     Log( TEXT( "End: %s\n\r\n\r\n" ), _tasctime( pTime ) );
173 
174     if ( m_pLogFile ) fclose( m_pLogFile );
175 
176     if ( m_pTmpName )
177     {
178         _tremove( m_pTmpName );
179         free( m_pTmpName );
180     }
181 
182     if ( m_pMSIErrorCode ) UnmapViewOfFile( m_pMSIErrorCode );
183     if ( m_hMapFile ) CloseHandle( m_hMapFile );
184 
185     if ( m_pAppTitle ) delete [] m_pAppTitle;
186     if ( m_pDatabase ) delete [] m_pDatabase;
187     if ( m_pReqVersion ) delete [] m_pReqVersion;
188     if ( m_pProductName ) delete [] m_pProductName;
189     if ( m_pAdvertise )   delete [] m_pAdvertise;
190     if ( m_pLogFile )     delete [] m_pLogFile;
191     if ( m_pErrorText )   delete [] m_pErrorText;
192     if ( m_pModuleFile )  delete [] m_pModuleFile;
193     if ( m_pPatchFiles )  delete [] m_pPatchFiles;
194     if ( m_pUpgradeKey )  delete [] m_pUpgradeKey;
195     if ( m_pProductVersion ) delete [] m_pProductVersion;
196 }
197 
198 //--------------------------------------------------------------------------
Initialize(HINSTANCE hInst)199 boolean SetupAppX::Initialize( HINSTANCE hInst )
200 {
201     m_pCmdLine  = WIN::GetCommandLine();
202     m_hInst     = hInst;
203 
204     // Load our AppTitle (caption)
205     m_pAppTitle     = new TCHAR[ MAX_STR_CAPTION ];
206     m_pAppTitle[0]  = '\0';
207     WIN::LoadString( hInst, IDS_APP_TITLE, m_pAppTitle, MAX_STR_CAPTION );
208 
209     // Obtain path we are running from
210     m_pModuleFile       = new TCHAR[ MAX_PATH ];
211     m_pModuleFile[ 0 ]  = '\0';
212 
213     if ( 0 == WIN::GetModuleFileName( hInst, m_pModuleFile, MAX_PATH ) )
214     {
215         SetError( WIN::GetLastError() );
216         return false;
217     }
218 
219     if ( ! GetCmdLineParameters( &m_pCmdLine ) )
220         return false;
221 
222     m_hMapFile = CreateFileMapping(
223                  INVALID_HANDLE_VALUE,      // use paging file
224                  NULL,                      // default security
225                  PAGE_READWRITE,            // read/write access
226                  0,                         // max. object size
227                  sizeof( int ),             // buffer size
228                  sMemMapName );
229     if ( m_hMapFile )
230     {
231         m_pMSIErrorCode = (int*) MapViewOfFile( m_hMapFile,  // handle to map object
232                         FILE_MAP_ALL_ACCESS,   // read/write permission
233                         0,
234                         0,
235                         sizeof( int ) );
236         if ( m_pMSIErrorCode )
237             *m_pMSIErrorCode = 0;
238         else
239             OutputDebugStringFormat( TEXT("Could not map view of file (%d).\n"), GetLastError() );
240     }
241     else
242         OutputDebugStringFormat( TEXT("Could not create file mapping object (%d).\n"), GetLastError() );
243 
244     Log( TEXT("Starting: %s\r\n"), m_pModuleFile );
245     Log( TEXT(" CommandLine=<%s>\r\n"), m_pCmdLine );
246 
247     if ( m_bQuiet )
248         Log( TEXT(" Using quiet install mode\r\n") );
249 
250     time_t aTime;
251     time( &aTime );
252     tm* pTime = localtime( &aTime );
253     Log( TEXT(" Begin: %s\n"), _tasctime( pTime ) );
254 
255     return true;
256 }
257 
258 //--------------------------------------------------------------------------
GetProfileSection(LPCTSTR pFileName,LPCTSTR pSection,DWORD & rSize,LPTSTR * pRetBuf)259 boolean SetupAppX::GetProfileSection( LPCTSTR pFileName, LPCTSTR pSection,
260                                       DWORD& rSize, LPTSTR *pRetBuf )
261 {
262     if ( !rSize || !*pRetBuf )
263     {
264         rSize = 512;
265         *pRetBuf = new TCHAR[ rSize ];
266     }
267 
268     DWORD nRet = GetPrivateProfileSection( pSection, *pRetBuf, rSize, pFileName );
269 
270     if ( nRet && ( nRet + 2 > rSize ) ) // buffer was too small, retry with bigger one
271     {
272         if ( nRet < 32767 - 2 )
273         {
274             delete [] (*pRetBuf);
275             rSize = nRet + 2;
276             *pRetBuf = new TCHAR[ rSize ];
277 
278             nRet = GetPrivateProfileSection( pSection, *pRetBuf, rSize, pFileName );
279         }
280     }
281 
282     if ( !nRet )
283     {
284         SetError( WIN::GetLastError() );
285 
286         TCHAR sBuf[80];
287         StringCchPrintf( sBuf, 80, TEXT("ERROR: GetPrivateProfileSection(): GetLastError returned %u\r\n"), GetError() );
288         Log( sBuf );
289         return false;
290     }
291     else if ( nRet + 2 > rSize )
292     {
293         SetError( ERROR_OUTOFMEMORY );
294         Log( TEXT( "ERROR: GetPrivateProfileSection() out of memory\r\n" ) );
295         return false;
296     }
297 
298     Log( TEXT( " GetProfileSection read %s\r\n" ), pSection );
299 
300     return true;
301 }
302 
303 //--------------------------------------------------------------------------
ReadProfile()304 boolean SetupAppX::ReadProfile()
305 {
306     boolean bRet = false;
307     TCHAR *sProfilePath = 0;
308 
309     if ( GetPathToFile( PROFILE_NAME, &sProfilePath ) )
310     {
311         DWORD nSize = 0;
312         LPTSTR pRetBuf = NULL;
313 
314         Log( TEXT( " Open ini file: <%s>\r\n" ), sProfilePath );
315 
316         bRet = GetProfileSection( sProfilePath, SECTION_SETUP, nSize, &pRetBuf );
317 
318         if ( !bRet )
319         {
320             LPTSTR pTmpFile = CopyIniFile( sProfilePath );
321             delete [] sProfilePath;
322             sProfilePath = pTmpFile;
323 
324             if ( sProfilePath )
325             {
326                 SetError( ERROR_SUCCESS );
327 
328                 Log( TEXT( " Could not open inifile, copied ini file to: <%s>\r\n" ), sProfilePath );
329                 bRet = GetProfileSection( sProfilePath, SECTION_SETUP, nSize, &pRetBuf );
330             }
331         }
332 
333         if ( bRet )
334         {
335             LPTSTR pCurLine = pRetBuf;
336             while ( *pCurLine )
337             {
338                 LPTSTR pName = 0;
339                 LPTSTR pValue = 0;
340 
341                 pCurLine += GetNameValue( pCurLine, &pName, &pValue );
342 
343                 if ( lstrcmpi( TEXT( "database" ), pName ) == 0 )
344                 {
345                     m_pDatabase = pValue;
346                     Log( TEXT( "    Database = %s\r\n" ), pValue );
347                 }
348                 else if ( lstrcmpi( TEXT( "msiversion" ), pName ) == 0 )
349                 {
350                     m_pReqVersion = pValue;
351                     Log( TEXT( "    msiversion = %s\r\n" ), pValue );
352                 }
353                 else if ( lstrcmpi( TEXT( "productname" ), pName ) == 0 )
354                 {
355                     m_pProductName = pValue;
356                     Log( TEXT( "    productname = %s\r\n" ), pValue );
357                     m_pAppTitle = SetProdToAppTitle( m_pProductName );
358                 }
359                 else if ( lstrcmpi( TEXT( "upgradekey" ), pName ) == 0 )
360                 {
361                     m_pUpgradeKey = pValue;
362                     Log( TEXT( "    upgradekey = %s\r\n" ), pValue );
363                 }
364                 else if ( lstrcmpi( TEXT( "productversion" ), pName ) == 0 )
365                 {
366                     m_pProductVersion = pValue;
367                     Log( TEXT( "    productversion = %s\r\n" ), pValue );
368                 }
369                 else if ( lstrcmpi( TEXT( "productcode" ), pName ) == 0 )
370                 {
371                     delete [] pValue;
372                 }
373                 else
374                 {
375                     Log( TEXT( "Warning: unknown entry in profile <%s>\r\n" ), pName );
376                     delete [] pValue;
377                 }
378             }
379         }
380 
381         if ( bRet && ( !m_pDatabase || !m_pReqVersion || !m_pProductName ) )
382         {
383             Log( TEXT( "ERROR: incomplete 'Setup' section in profile\r\n" ) );
384             SetError( ERROR_INVALID_DATA );
385             bRet = false;
386         }
387 
388         if ( bRet )
389             bRet = GetProfileSection( sProfilePath, SECTION_LANGUAGE, nSize, &pRetBuf );
390 
391         if ( bRet )
392         {
393             LPTSTR pName = 0;
394             LPTSTR pValue = 0;
395             LPTSTR pCurLine = pRetBuf;
396             LPTSTR pLastChar;
397             int  nNext = 0;
398 
399             // first line in this section should be the language count
400             nNext = GetNameValue( pCurLine, &pName, &pValue );
401             if ( lstrcmpi( TEXT( "count" ), pName ) == 0 )
402             {
403                 Log( TEXT( "    Languages = %s\r\n" ), pValue );
404                 m_nLanguageCount = _tcstol( pValue, &pLastChar, 10 );
405                 pCurLine += nNext;
406                 delete [] pValue;
407             }
408 
409             m_ppLanguageList = new LanguageDataX*[ m_nLanguageCount ];
410 
411             for ( int i=0; i < m_nLanguageCount; i++ )
412             {
413                 if ( !*pCurLine )
414                 {
415                     m_nLanguageCount = i;
416                     break;
417                 }
418 
419                 pCurLine += GetNameValue( pCurLine, &pName, &pValue );
420                 m_ppLanguageList[ i ] = new LanguageDataX( pValue );
421                 Log( TEXT( "    Language = %s\r\n" ), pValue );
422 
423                 if ( m_ppLanguageList[ i ]->m_pTransform )
424                     Log( TEXT( "      Transform = %s\r\n" ), m_ppLanguageList[ i ]->m_pTransform );
425 
426                 delete [] pValue;
427             }
428         }
429 
430         if ( pRetBuf )
431             delete [] pRetBuf;
432     }
433 
434     if ( sProfilePath && ! m_pTmpName )
435         delete [] sProfilePath;
436 
437     return bRet;
438 }
439 
440 //--------------------------------------------------------------------------
AddFileToPatchList(TCHAR * pPath,TCHAR * pFile)441 void SetupAppX::AddFileToPatchList( TCHAR* pPath, TCHAR* pFile )
442 {
443     if ( m_pPatchFiles == NULL )
444     {
445         m_pPatchFiles = new TCHAR[ MAX_STR_LENGTH ];
446         StringCchCopy( m_pPatchFiles, MAX_STR_LENGTH, TEXT("\"") );
447     }
448     else
449         StringCchCat( m_pPatchFiles, MAX_STR_LENGTH, TEXT(";") );
450 
451     StringCchCat( m_pPatchFiles, MAX_STR_LENGTH, pPath );
452     StringCchCat( m_pPatchFiles, MAX_STR_LENGTH, pFile );
453 }
454 
455 //--------------------------------------------------------------------------
GetPatches()456 boolean SetupAppX::GetPatches()
457 {
458     boolean bRet = true;
459 
460     int nPatternLen = lstrlen( m_pModuleFile ) + 7; // 1 for null terminator, 1 for back slash, 5 for extensions
461     TCHAR* pPattern = new TCHAR[ nPatternLen ];
462     TCHAR* pBaseDir = new TCHAR[ nPatternLen ];
463 
464     // find 'setup.exe' in the path so we can remove it
465     TCHAR *pFilePart = 0;
466     if ( 0 == GetFullPathName( m_pModuleFile, nPatternLen, pPattern, &pFilePart ) )
467     {
468         SetError( WIN::GetLastError() );
469         bRet = false;
470     }
471     else
472     {
473         if ( pFilePart )
474             *pFilePart = '\0';
475         StringCchCopy( pBaseDir, nPatternLen, pPattern );
476         StringCchCat( pPattern, nPatternLen, TEXT("*.msp") );
477 
478         WIN32_FIND_DATA aFindFileData;
479 
480         HANDLE hFindPatches = FindFirstFile( pPattern, &aFindFileData );
481 
482         if ( hFindPatches != INVALID_HANDLE_VALUE )
483         {
484             if ( ! IsPatchInstalled( pBaseDir, aFindFileData.cFileName ) )
485                 AddFileToPatchList( pBaseDir, aFindFileData.cFileName );
486 
487             while ( FindNextFile( hFindPatches, &aFindFileData ) )
488             {
489                 if ( ! IsPatchInstalled( pBaseDir, aFindFileData.cFileName ) )
490                     AddFileToPatchList( pBaseDir, aFindFileData.cFileName );
491             }
492 
493             if ( m_pPatchFiles != NULL )
494                 StringCchCat( m_pPatchFiles, MAX_STR_LENGTH, TEXT("\"") );
495 
496             FindClose( hFindPatches );
497         }
498     }
499 
500     delete [] pPattern;
501     delete [] pBaseDir;
502 
503     return bRet;
504 }
505 
506 //--------------------------------------------------------------------------
GetPathToFile(TCHAR * pFileName,TCHAR ** pPath)507 boolean SetupAppX::GetPathToFile( TCHAR* pFileName, TCHAR** pPath )
508 {
509     // generate the path to the file = szModuleFile + FileName
510     // note: FileName is a relative path
511 
512     boolean bRet = true;
513 
514     int nTempPath = lstrlen( m_pModuleFile ) + lstrlen( pFileName ) + 2; // 1 for null terminator, 1 for back slash
515     TCHAR* pTempPath = new TCHAR[ nTempPath ];
516 
517     // find 'setup.exe' in the path so we can remove it
518     TCHAR *pFilePart = 0;
519     if ( 0 == GetFullPathName( m_pModuleFile, nTempPath, pTempPath, &pFilePart ) )
520     {
521         SetError( WIN::GetLastError() );
522         bRet = false;
523     }
524     else
525     {
526         if ( pFilePart )
527             *pFilePart = '\0';
528 
529         StringCchCat( pTempPath, nTempPath, pFileName );
530 
531         int nPath = 2 * nTempPath;
532         *pPath = new TCHAR[ nPath ];
533 
534         // normalize the path
535         int nReturn = GetFullPathName( pTempPath, nPath, *pPath, &pFilePart );
536 
537         if ( nReturn > nPath )
538         {
539             // try again, with larger buffer
540             delete [] (*pPath);
541             nPath = nReturn;
542             *pPath = new TCHAR[ nPath ];
543 
544             nReturn = GetFullPathName( pTempPath, nPath, *pPath, &pFilePart );
545         }
546 
547         if ( 0 == nReturn )
548         {
549             // error -- invalid path
550             SetError( WIN::GetLastError() );
551             bRet = false;
552         }
553     }
554 
555     if ( bRet ) // check for the file's existence
556     {
557         DWORD dwFileAttrib = GetFileAttributes( *pPath );
558 
559         if (0xFFFFFFFF == dwFileAttrib)
560         {
561             StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pFileName );
562             SetError( ERROR_FILE_NOT_FOUND );
563             bRet = false;
564         }
565     }
566 
567     delete [] pTempPath;
568     return bRet;
569 }
570 
571 //--------------------------------------------------------------------------
GetNameValue(TCHAR * pLine,TCHAR ** pName,TCHAR ** pValue)572 int SetupAppX::GetNameValue( TCHAR* pLine, TCHAR** pName, TCHAR** pValue )
573 {
574     int nRet = lstrlen( pLine ) + 1;
575     *pValue = 0;
576 
577     if ( nRet == 1 )
578         return nRet;
579 
580     LPTSTR pChar = pLine;
581     LPTSTR pLast = NULL;
582 
583     // Skip leading spaces.
584     while (' ' == *pChar || '\t' == *pChar)
585         pChar = CharNext( pChar );
586 
587     *pName = pChar;
588 
589     // look for the end of the name
590     while( *pChar && (' ' != *pChar) &&
591            ( '\t' != *pChar ) && ( '=' != *pChar ) )
592         pChar = CharNext( pChar );
593 
594     if ( ! *pChar )
595         return nRet;
596 
597     pLast = pChar;
598     pChar = CharNext( pChar );
599     *pLast = '\0';
600 
601     // look for the start of the value
602     while( ( ' ' == *pChar ) || ( '\t' == *pChar ) ||
603            ( '=' == *pChar ) )
604         pChar = CharNext( pChar );
605 
606     int nValueLen = lstrlen( pChar ) + 1;
607     *pValue = new TCHAR[ nValueLen ];
608 
609     if ( *pValue )
610         StringCchCopy( *pValue, nValueLen, pChar );
611 
612     return nRet;
613 }
614 
615 //--------------------------------------------------------------------------
ChooseLanguage(long & rLanguage)616 boolean SetupAppX::ChooseLanguage( long& rLanguage )
617 {
618     rLanguage = 0;
619 
620     if ( m_bQuiet )
621         return true;
622 
623     // When there are none or only one language, there is nothing
624     // to do here
625     if ( m_nLanguageCount > 1 )
626     {
627         TCHAR *sString = new TCHAR[ MAX_LANGUAGE_LEN ];
628 
629         LANGID nUserDefLang = GetUserDefaultLangID();
630         LANGID nSysDefLang = GetSystemDefaultLangID();
631 
632         int nUserPrimary = PRIMARYLANGID( nUserDefLang );
633         int nSysPrimary = PRIMARYLANGID( nSysDefLang );
634 
635         long nUserIndex = -1;
636         long nUserPrimIndex = -1;
637         long nSystemIndex = -1;
638         long nSystemPrimIndex = -1;
639         long nParamIndex = -1;
640 
641         for ( long i=0; i<GetLanguageCount(); i++ )
642         {
643             long nLanguage = GetLanguageID( i );
644             int nPrimary = PRIMARYLANGID( nLanguage );
645             GetLanguageName( nLanguage, sString );
646             Log( TEXT( "    Info: found Language: %s\r\n" ), sString );
647 
648             if ( nLanguage == nUserDefLang )
649                 nUserIndex = i;
650             if ( nPrimary == nUserPrimary )
651                 nUserPrimIndex = i;
652             if ( nLanguage == nSysDefLang )
653                 nSystemIndex = i;
654             if ( nPrimary == nSysPrimary )
655                 nSystemPrimIndex = i;
656             if ( m_nLanguageID && ( nLanguage == m_nLanguageID ) )
657                 nParamIndex = i;
658         }
659 
660         if ( m_nLanguageID && ( nParamIndex == -1 ) )
661         {
662             Log( TEXT( "Warning: Language chosen with parameter -lang not found.\r\n" ) );
663         }
664 
665         if ( nParamIndex != -1 )
666         {
667             Log( TEXT( "Info: Found language chosen with parameter -lang.\r\n" ) );
668             rLanguage = GetLanguageID( nParamIndex );
669         }
670         else if ( nUserIndex != -1 )
671         {
672             Log( TEXT( "Info: Found user default language.\r\n" ) );
673             rLanguage = GetLanguageID( nUserIndex );
674         }
675         else if ( nUserPrimIndex != -1 )
676         {
677             Log( TEXT( "Info: Found user default primary language.\r\n" ) );
678             rLanguage = GetLanguageID( nUserPrimIndex );
679         }
680         else if ( nSystemIndex != -1 )
681         {
682             Log( TEXT( "Info: Found system default language.\r\n" ) );
683             rLanguage = GetLanguageID( nSystemIndex );
684         }
685         else if ( nSystemPrimIndex != -1 )
686         {
687             Log( TEXT( "Info: Found system default primary language.\r\n" ) );
688             rLanguage = GetLanguageID( nSystemPrimIndex );
689         }
690         else
691         {
692             Log( TEXT( "Info: Use default language from ini file.\r\n" ) );
693             rLanguage = GetLanguageID( 0 );
694         }
695         delete [] sString;
696     }
697 
698     return true;
699 }
700 
701 //--------------------------------------------------------------------------
LoadMsiLibrary()702 HMODULE SetupAppX::LoadMsiLibrary()
703 {
704     HMODULE hMsi = NULL;
705     HKEY    hInstKey = NULL;
706 
707     // find registered location of Msi.dll
708     if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, sInstKey, 0, KEY_READ, &hInstKey ) )
709     {
710         long    nRet = ERROR_SUCCESS;
711         TCHAR  *sMsiFolder = new TCHAR[ MAX_PATH + 1 ];
712         DWORD   dwMsiFolderSize = MAX_PATH + 1;
713         DWORD   dwType = 0;
714 
715         if ( ERROR_MORE_DATA == ( nRet = RegQueryValueEx( hInstKey, sInstLocValue, NULL,
716                                                           &dwType, (BYTE*)sMsiFolder, &dwMsiFolderSize ) ) )
717         {
718             // try again with larger buffer
719             delete [] sMsiFolder;
720             sMsiFolder = new TCHAR[ dwMsiFolderSize ];
721 
722             nRet = RegQueryValueEx( hInstKey, sInstLocValue, NULL, &dwType,
723                                     (BYTE*)sMsiFolder, &dwMsiFolderSize );
724         }
725 
726         if ( ERROR_SUCCESS == nRet && dwType == REG_SZ && dwMsiFolderSize > 0 )
727         {
728             // load Msi.dll from registered location
729             int nLength = lstrlen( sMsiDll ) + dwMsiFolderSize + 1; // use StringCchLength ?
730             TCHAR *pMsiLocation = new TCHAR[ nLength ];
731 
732             if ( SUCCEEDED( StringCchCopy( pMsiLocation, nLength, sMsiFolder ) ) &&
733                  SUCCEEDED( StringCchCat( pMsiLocation, nLength, sMsiDll ) ) )
734             {
735                 hMsi = LoadLibrary( pMsiLocation );
736             }
737         }
738     }
739 
740     if ( !hMsi ) // use the default location
741     {
742         hMsi = LoadLibrary( sMsiDll );
743     }
744 
745     return hMsi;
746 }
747 
748 //--------------------------------------------------------------------------
GetPathToMSI()749 LPCTSTR SetupAppX::GetPathToMSI()
750 {
751     LPTSTR  sMsiPath = NULL;
752     HKEY    hInstKey = NULL;
753     TCHAR  *sMsiFolder = new TCHAR[ MAX_PATH + 1 ];
754     DWORD   nMsiFolderSize = MAX_PATH + 1;
755 
756     sMsiFolder[0] = '\0';
757 
758     // find registered location of Msi.dll
759     if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, sInstKey, 0, KEY_READ, &hInstKey ) )
760     {
761         LONG    nRet = ERROR_SUCCESS;
762         DWORD   dwType = 0;
763 
764         if ( ERROR_MORE_DATA == ( nRet = RegQueryValueEx( hInstKey, sInstLocValue, NULL,
765                                                           &dwType, (BYTE*)sMsiFolder, &nMsiFolderSize ) ) )
766         {
767             // try again with larger buffer
768             delete [] sMsiFolder;
769             sMsiFolder = new TCHAR[ nMsiFolderSize ];
770 
771             nRet = RegQueryValueEx( hInstKey, sInstLocValue, NULL, &dwType,
772                                     (BYTE*)sMsiFolder, &nMsiFolderSize );
773         }
774 
775         if ( ERROR_SUCCESS != nRet || dwType != REG_SZ || nMsiFolderSize == 0 )
776             sMsiFolder[0] = '\0';
777     }
778 
779     if ( sMsiFolder[0] == '\0' ) // use the default location
780     {
781         Log( TEXT( "  Could not find path to msiexec.exe in registry" ) );
782 
783         DWORD nRet = WIN::GetSystemDirectory( sMsiFolder, nMsiFolderSize );
784         if ( nRet > nMsiFolderSize )
785         {
786             delete [] sMsiFolder;
787             sMsiFolder = new TCHAR[ nRet ];
788             nMsiFolderSize = nRet;
789 
790             nRet = WIN::GetSystemDirectory( sMsiFolder, nMsiFolderSize );
791         }
792         if ( 0 == nRet )
793         {
794             sMsiFolder[0] = '\0';
795             SetError( WIN::GetLastError() );
796         }
797         nMsiFolderSize = nRet;
798     }
799 
800     if ( sMsiFolder[0] != '\0' )
801     {
802         int nLength = lstrlen( sMsiExe ) + lstrlen( sMsiFolder ) + 1;
803         sMsiPath = new TCHAR[ nLength ];
804 
805         if ( FAILED( StringCchCopy( sMsiPath, nLength, sMsiFolder ) ) ||
806              FAILED( StringCchCat( sMsiPath, nLength, sMsiExe ) ) )
807         {
808             delete [] sMsiPath;
809             sMsiPath = NULL;
810         }
811     }
812 
813     if ( ! sMsiPath )
814         Log( TEXT( "ERROR: Can't build path to msiexec.exe!" ) );
815 
816     return sMsiPath;
817 }
818 
819 //--------------------------------------------------------------------------
LaunchInstaller(LPCTSTR pParam)820 boolean SetupAppX::LaunchInstaller( LPCTSTR pParam )
821 {
822     LPCTSTR sMsiPath = GetPathToMSI();
823 
824     if ( !sMsiPath )
825     {
826         Log( TEXT( "ERROR: msiexec not found!" ) );
827         SetError( ERROR_FILE_NOT_FOUND );
828         return false;
829     }
830 
831     STARTUPINFO         aSUI;
832     PROCESS_INFORMATION aPI;
833 
834     Log( TEXT( " Will install using <%s>\r\n" ), sMsiPath );
835     Log( TEXT( "   Parameters are: %s\r\n" ), pParam );
836 
837     OutputDebugStringFormat( TEXT( " Will install using <%s>\r\n" ), sMsiPath );
838     OutputDebugStringFormat( TEXT( "   Parameters are: %s\r\n" ), pParam );
839 
840     ZeroMemory( (void*)&aPI, sizeof( PROCESS_INFORMATION ) );
841     ZeroMemory( (void*)&aSUI, sizeof( STARTUPINFO ) );
842 
843     aSUI.cb          = sizeof(STARTUPINFO);
844     aSUI.dwFlags     = STARTF_USESHOWWINDOW;
845     aSUI.wShowWindow = SW_SHOW;
846 
847     DWORD nCmdLineLength = lstrlen( sMsiPath ) + lstrlen( pParam ) + 2;
848     TCHAR *sCmdLine = new TCHAR[ nCmdLineLength ];
849 
850     if ( FAILED( StringCchCopy( sCmdLine, nCmdLineLength, sMsiPath ) ) ||
851          FAILED( StringCchCat(  sCmdLine, nCmdLineLength, TEXT( " " ) ) ) ||
852          FAILED( StringCchCat(  sCmdLine, nCmdLineLength, pParam ) ) )
853     {
854         delete [] sCmdLine;
855         SetError( ERROR_INSTALL_FAILURE );
856         return false;
857     }
858 
859     if ( !WIN::CreateProcess( NULL, sCmdLine, NULL, NULL, FALSE,
860                               CREATE_DEFAULT_ERROR_MODE, NULL, NULL,
861                               &aSUI, &aPI ) )
862     {
863         Log( TEXT( "ERROR: Could not create process %s.\r\n" ), sCmdLine );
864         SetError( WIN::GetLastError() );
865         delete [] sCmdLine;
866         return false;
867     }
868 
869     DWORD nResult = WaitForProcess( aPI.hProcess );
870     bool bRet = true;
871 
872     if( ERROR_SUCCESS != nResult )
873     {
874         Log( TEXT( "ERROR: While waiting for %s.\r\n" ), sCmdLine );
875         SetError( nResult );
876         bRet = false;
877     }
878     else
879     {
880         GetExitCodeProcess( aPI.hProcess, &nResult );
881         SetError( nResult );
882 
883         if ( nResult != ERROR_SUCCESS )
884         {
885             TCHAR sBuf[80];
886             StringCchPrintf( sBuf, 80, TEXT("Warning: msiexec returned %u.\r\n"), nResult );
887             Log( sBuf );
888         }
889         else
890             Log( TEXT( " Installation completed successfully.\r\n" ) );
891     }
892 
893     CloseHandle( aPI.hProcess );
894 
895     delete [] sCmdLine;
896 
897     return bRet;
898 }
899 
900 //--------------------------------------------------------------------------
Install(long nLanguage)901 boolean SetupAppX::Install( long nLanguage )
902 {
903     LPTSTR pTransform = NULL;
904 
905     if ( nLanguage ) // look for transformation
906     {
907         for ( int i = 0; i < m_nLanguageCount; i++ )
908         {
909             if ( m_ppLanguageList[i]->m_nLanguageID == nLanguage )
910             {
911                 if ( m_ppLanguageList[i]->m_pTransform )
912                 {
913                     if ( !GetPathToFile( m_ppLanguageList[i]->m_pTransform,
914                                         &pTransform ) )
915                     {
916                         Log( TEXT( "ERROR: Could not find transform <%s\r\n" ), m_ppLanguageList[i]->m_pTransform );
917                         return false;
918                     }
919                 }
920                 break;
921             }
922         }
923     }
924 
925     TCHAR *pDataBasePath = NULL;
926 
927     if ( ! GetPathToFile( m_pDatabase, &pDataBasePath ) )
928     {
929         Log( TEXT( "ERROR: Could not find database <%s\r\n" ), m_pDatabase );
930         SetError( ERROR_INSTALL_SOURCE_ABSENT );
931         return false;
932     }
933 
934     // we will always use the parameter setup used
935     int nParLen = lstrlen( PARAM_SETUP_USED );
936 
937     if ( m_bRegNoMsoTypes )
938         nParLen += lstrlen( PARAM_REG_NO_MSO_TYPES );
939     else if ( m_bRegAllMsoTypes )
940         nParLen += lstrlen( PARAM_REG_ALL_MSO_TYPES );
941 
942     if ( m_pAdvertise )
943         nParLen += lstrlen( m_pAdvertise ) + 1;     // one for the space
944     else if ( m_bIsMinorUpgrade )
945         nParLen += lstrlen( PARAM_MINOR_UPGRADE );
946     else
947         nParLen += lstrlen( PARAM_PACKAGE );
948 
949     nParLen += lstrlen( pDataBasePath ) + 3;        // two quotes, one null
950 
951     if ( NeedReboot() )
952         nParLen += lstrlen( PARAM_REBOOT );
953 
954     if ( m_pPatchFiles )
955     {
956         nParLen += lstrlen( PARAM_PATCH );
957         nParLen += lstrlen( m_pPatchFiles );
958     }
959 
960     if ( pTransform )
961     {
962         nParLen += lstrlen( PARAM_TRANSFORM );
963         nParLen += lstrlen( pTransform ) + 2;       // two quotes
964     }
965 
966     if ( m_pCmdLine )
967         nParLen += lstrlen( m_pCmdLine ) + 1;       // one for the space;
968 
969     TCHAR *pParams = new TCHAR[ nParLen ];
970 
971     StringCchCopy( pParams, nParLen, PARAM_SETUP_USED );
972 
973     if ( m_bRegNoMsoTypes )
974         StringCchCat( pParams, nParLen, PARAM_REG_NO_MSO_TYPES );
975     else if ( m_bRegAllMsoTypes )
976         StringCchCat( pParams, nParLen, PARAM_REG_ALL_MSO_TYPES );
977 
978     if ( m_pAdvertise )
979         StringCchCat( pParams, nParLen, m_pAdvertise );
980     else if ( IsAdminInstall() )
981         StringCchCat( pParams, nParLen, PARAM_ADMIN );
982     else if ( m_bIsMinorUpgrade )
983         StringCchCat( pParams, nParLen, PARAM_MINOR_UPGRADE );
984     else
985         StringCchCat( pParams, nParLen, PARAM_PACKAGE );
986 
987     StringCchCat( pParams, nParLen, TEXT( "\"" ) );
988     StringCchCat( pParams, nParLen, pDataBasePath );
989     StringCchCat( pParams, nParLen, TEXT( "\"" ) );
990 
991     if ( NeedReboot() )
992         StringCchCat( pParams, nParLen, PARAM_REBOOT );
993 
994     if ( m_pPatchFiles )
995     {
996         StringCchCat( pParams, nParLen, PARAM_PATCH );
997         StringCchCat( pParams, nParLen, m_pPatchFiles );
998     }
999 
1000     if ( pTransform )
1001     {
1002         StringCchCat( pParams, nParLen, PARAM_TRANSFORM );
1003         StringCchCat( pParams, nParLen, TEXT( "\"" ) );
1004         StringCchCat( pParams, nParLen, pTransform );
1005         StringCchCat( pParams, nParLen, TEXT( "\"" ) );
1006     }
1007 
1008     if ( m_pCmdLine )
1009     {
1010         StringCchCat( pParams, nParLen, TEXT( " " ) );
1011         StringCchCat( pParams, nParLen, m_pCmdLine );
1012     }
1013 
1014     return LaunchInstaller( pParams );
1015 }
1016 
1017 //--------------------------------------------------------------------------
GetError() const1018 UINT SetupAppX::GetError() const
1019 {
1020     UINT nErr = 0;
1021 
1022     if ( m_pMSIErrorCode )
1023         nErr = (UINT) *m_pMSIErrorCode;
1024 
1025     if ( nErr == 0 )
1026         nErr = m_uiRet;
1027 
1028     if ( nErr != 0 )
1029         OutputDebugStringFormat( TEXT("Setup will return error (%d).\n"), nErr );
1030     return nErr;
1031 }
1032 
1033 //--------------------------------------------------------------------------
DisplayError(UINT nErr) const1034 void SetupAppX::DisplayError( UINT nErr ) const
1035 {
1036     TCHAR sError[ MAX_TEXT_LENGTH ] = {0};
1037     TCHAR sTmp[ MAX_TEXT_LENGTH ] = {0};
1038 
1039     UINT  nMsgType = MB_OK | MB_ICONERROR;
1040 
1041     switch ( nErr )
1042     {
1043         case ERROR_SUCCESS:     break;  // 0
1044 
1045         case ERROR_FILE_NOT_FOUND:  // 2
1046                                 WIN::LoadString( m_hInst, IDS_FILE_NOT_FOUND, sTmp, MAX_TEXT_LENGTH );
1047                                 StringCchPrintf( sError, MAX_TEXT_LENGTH, sTmp, m_pErrorText );
1048                                 break;
1049         case ERROR_INVALID_DATA:    // 13
1050                                 WIN::LoadString( m_hInst, IDS_INVALID_PROFILE, sError, MAX_TEXT_LENGTH );
1051                                 break;
1052         case ERROR_OUTOFMEMORY: WIN::LoadString( m_hInst, IDS_OUTOFMEM, sError, MAX_TEXT_LENGTH );
1053                                 break;
1054         case ERROR_INSTALL_USEREXIT:
1055                                 WIN::LoadString( m_hInst, IDS_USER_CANCELLED, sError, MAX_TEXT_LENGTH );
1056                                 break;
1057         case ERROR_INSTALL_ALREADY_RUNNING: // 1618
1058                                 WIN::LoadString( m_hInst, IDS_ALREADY_RUNNING, sError, MAX_TEXT_LENGTH );
1059                                 break;
1060         case ERROR_INSTALL_SOURCE_ABSENT:
1061                                 WIN::LoadString( m_hInst, IDS_NOMSI, sError, MAX_TEXT_LENGTH );
1062                                 break;
1063         case ERROR_DS_INSUFF_ACCESS_RIGHTS: // 8344
1064                                 WIN::LoadString( m_hInst, IDS_REQUIRES_ADMIN_PRIV, sError, MAX_TEXT_LENGTH );
1065                                 break;
1066         case E_ABORT:           WIN::LoadString( m_hInst, IDS_UNKNOWN_ERROR, sError, MAX_TEXT_LENGTH );
1067                                 break;
1068         case ERROR_INVALID_PARAMETER:   // 87
1069                                 WIN::LoadString( m_hInst, IDS_INVALID_PARAM, sTmp, MAX_TEXT_LENGTH );
1070                                 StringCchPrintf( sError, MAX_TEXT_LENGTH, sTmp, m_pErrorText );
1071                                 break;
1072 
1073         case ERROR_SETUP_TO_OLD:    // - 3
1074                                 WIN::LoadString( m_hInst, IDS_SETUP_TO_OLD, sTmp, MAX_TEXT_LENGTH );
1075                                 StringCchPrintf( sError, MAX_TEXT_LENGTH, sTmp, m_pReqVersion, m_pErrorText );
1076                                 break;
1077         case ERROR_SETUP_NOT_FOUND: // - 4
1078                                 WIN::LoadString( m_hInst, IDS_SETUP_NOT_FOUND, sTmp, MAX_TEXT_LENGTH );
1079                                 StringCchPrintf( sError, MAX_TEXT_LENGTH, sTmp, m_pReqVersion );
1080                                 break;
1081         case ERROR_SHOW_USAGE:      // - 2
1082                                 nMsgType = MB_OK | MB_ICONINFORMATION;
1083                                 WIN::LoadString( m_hInst, IDS_USAGE, sError, MAX_TEXT_LENGTH );
1084                                 break;
1085 
1086         default:                WIN::LoadString( m_hInst, IDS_UNKNOWN_ERROR, sError, MAX_TEXT_LENGTH );
1087                                 break;
1088     }
1089 
1090     if ( sError[0] )
1091     {
1092         if ( !m_bQuiet )
1093         {
1094             ConvertNewline( sError );
1095             WIN::MessageBox( NULL, sError, m_pAppTitle, nMsgType );
1096         }
1097 
1098         Log( TEXT( "ERROR: %s\r\n" ), sError );
1099     }
1100 }
1101 
1102 //--------------------------------------------------------------------------
GetLanguageID(long nIndex) const1103 long SetupAppX::GetLanguageID( long nIndex ) const
1104 {
1105     if ( nIndex >=0 && nIndex < m_nLanguageCount )
1106         return m_ppLanguageList[ nIndex ]->m_nLanguageID;
1107     else
1108         return 0;
1109 }
1110 
1111 //--------------------------------------------------------------------------
GetLanguageName(long nLanguage,LPTSTR sName) const1112 void SetupAppX::GetLanguageName( long nLanguage, LPTSTR sName ) const
1113 {
1114     switch ( nLanguage )
1115     {
1116         case 1028: WIN::LoadString( m_hInst, IDS_LANGUAGE_ZH_TW, sName, MAX_LANGUAGE_LEN ); break;
1117         case 1029: WIN::LoadString( m_hInst, IDS_LANGUAGE_CS,    sName, MAX_LANGUAGE_LEN ); break;
1118         case 1030: WIN::LoadString( m_hInst, IDS_LANGUAGE_DA,    sName, MAX_LANGUAGE_LEN ); break;
1119         case 1031: WIN::LoadString( m_hInst, IDS_LANGUAGE_DE_DE, sName, MAX_LANGUAGE_LEN ); break;
1120         case 1032: WIN::LoadString( m_hInst, IDS_LANGUAGE_EL,    sName, MAX_LANGUAGE_LEN ); break;
1121         case 1033: WIN::LoadString( m_hInst, IDS_LANGUAGE_EN_US, sName, MAX_LANGUAGE_LEN ); break;
1122         case 1034: WIN::LoadString( m_hInst, IDS_LANGUAGE_ES,    sName, MAX_LANGUAGE_LEN ); break;
1123         case 1035: WIN::LoadString( m_hInst, IDS_LANGUAGE_FI,    sName, MAX_LANGUAGE_LEN ); break;
1124         case 1036: WIN::LoadString( m_hInst, IDS_LANGUAGE_FR_FR, sName, MAX_LANGUAGE_LEN ); break;
1125         case 1037: WIN::LoadString( m_hInst, IDS_LANGUAGE_HE,    sName, MAX_LANGUAGE_LEN ); break;
1126         case 1038: WIN::LoadString( m_hInst, IDS_LANGUAGE_HU,    sName, MAX_LANGUAGE_LEN ); break;
1127         case 1040: WIN::LoadString( m_hInst, IDS_LANGUAGE_IT_IT, sName, MAX_LANGUAGE_LEN ); break;
1128         case 1041: WIN::LoadString( m_hInst, IDS_LANGUAGE_JA,    sName, MAX_LANGUAGE_LEN ); break;
1129         case 1042: WIN::LoadString( m_hInst, IDS_LANGUAGE_KO,    sName, MAX_LANGUAGE_LEN ); break;
1130         case 1043: WIN::LoadString( m_hInst, IDS_LANGUAGE_NL_NL, sName, MAX_LANGUAGE_LEN ); break;
1131         case 1044: WIN::LoadString( m_hInst, IDS_LANGUAGE_NO_NO, sName, MAX_LANGUAGE_LEN ); break;
1132         case 1045: WIN::LoadString( m_hInst, IDS_LANGUAGE_PL,    sName, MAX_LANGUAGE_LEN ); break;
1133         case 1046: WIN::LoadString( m_hInst, IDS_LANGUAGE_PT_BR, sName, MAX_LANGUAGE_LEN ); break;
1134         case 1049: WIN::LoadString( m_hInst, IDS_LANGUAGE_RU,    sName, MAX_LANGUAGE_LEN ); break;
1135         case 1051: WIN::LoadString( m_hInst, IDS_LANGUAGE_SK,    sName, MAX_LANGUAGE_LEN ); break;
1136         case 1053: WIN::LoadString( m_hInst, IDS_LANGUAGE_SV_SE, sName, MAX_LANGUAGE_LEN ); break;
1137         case 1054: WIN::LoadString( m_hInst, IDS_LANGUAGE_TH,    sName, MAX_LANGUAGE_LEN ); break;
1138         case 1055: WIN::LoadString( m_hInst, IDS_LANGUAGE_TR,    sName, MAX_LANGUAGE_LEN ); break;
1139         case 1061: WIN::LoadString( m_hInst, IDS_LANGUAGE_ET,    sName, MAX_LANGUAGE_LEN ); break;
1140         case 2052: WIN::LoadString( m_hInst, IDS_LANGUAGE_ZH_CN, sName, MAX_LANGUAGE_LEN ); break;
1141         case 2070: WIN::LoadString( m_hInst, IDS_LANGUAGE_PT_PT, sName, MAX_LANGUAGE_LEN ); break;
1142 
1143         default:
1144             {
1145                 TCHAR sTmp[ MAX_LANGUAGE_LEN ] = {0};
1146 
1147                 WIN::LoadString( m_hInst, IDS_UNKNOWN_LANG, sTmp, MAX_LANGUAGE_LEN );
1148                 StringCchPrintf( sName, MAX_LANGUAGE_LEN, sTmp, nLanguage );
1149             }
1150     }
1151 }
1152 
1153 //--------------------------------------------------------------------------
CheckVersion()1154 boolean SetupAppX::CheckVersion()
1155 {
1156     boolean bRet = false;
1157     HMODULE hMsi = LoadMsiLibrary();
1158 
1159     Log( TEXT( " Looking for installed MSI with version >= %s\r\n" ), m_pReqVersion );
1160 
1161     if ( !hMsi )
1162     {
1163         Log( TEXT( "Error: No MSI found!\r\n" ) );
1164         SetError( (UINT) ERROR_SETUP_NOT_FOUND );
1165     }
1166     else
1167     {
1168         PFnDllGetVersion pDllGetVersion = (PFnDllGetVersion) GetProcAddress( hMsi, MSIAPI_DllGetVersion );
1169 
1170         if ( pDllGetVersion )
1171         {
1172             DLLVERSIONINFO aInfo;
1173 
1174             aInfo.cbSize = sizeof( DLLVERSIONINFO );
1175             if ( NOERROR == pDllGetVersion( &aInfo ) )
1176             {
1177                 TCHAR pMsiVersion[ VERSION_SIZE ];
1178                 StringCchPrintf( pMsiVersion, VERSION_SIZE, TEXT("%d.%d.%4d"),
1179                                  aInfo.dwMajorVersion,
1180                                  aInfo.dwMinorVersion,
1181                                  aInfo.dwBuildNumber );
1182                 if ( _tcsncmp( pMsiVersion, m_pReqVersion, _tcslen( pMsiVersion ) ) < 0 )
1183                 {
1184                     StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pMsiVersion );
1185                     SetError( (UINT) ERROR_SETUP_TO_OLD );
1186                     Log( TEXT( "Warning: Old MSI version found <%s>, update needed!\r\n" ), pMsiVersion );
1187                 }
1188                 else
1189                 {
1190                     Log( TEXT( " Found MSI version <%s>, no update needed\r\n" ), pMsiVersion );
1191                     bRet = true;
1192                 }
1193                 if ( aInfo.dwMajorVersion >= 3 )
1194                     m_bSupportsPatch = true;
1195                 else
1196                     Log( TEXT("Warning: Patching not supported! MSI-Version <%s>\r\n"), pMsiVersion );
1197             }
1198         }
1199 
1200         FreeLibrary( hMsi );
1201     }
1202 
1203     return bRet;
1204 }
1205 
1206 //--------------------------------------------------------------------------
CheckForUpgrade()1207 boolean SetupAppX::CheckForUpgrade()
1208 {
1209     // When we have patch files we will never try an Minor upgrade
1210     if ( m_pPatchFiles ) return true;
1211 
1212     if ( !m_pUpgradeKey || ( _tcslen( m_pUpgradeKey ) == 0 ) )
1213     {
1214         Log( TEXT( "    No Upgrade Key Found -> continue with standard installation!\r\n" ) );
1215         return true;
1216     }
1217 
1218     HKEY hInstKey = NULL;
1219 
1220     if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, m_pUpgradeKey, 0, KEY_READ, &hInstKey ) )
1221     {
1222         Log( TEXT( " Found Upgrade Key in Registry (HKLM) -> will try minor upgrade!\r\n" ) );
1223         m_bIsMinorUpgrade = true;
1224     }
1225     else if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_CURRENT_USER, m_pUpgradeKey, 0, KEY_READ, &hInstKey ) )
1226     {
1227         Log( TEXT( " Found Upgrade Key in Registry (HKCU) -> will try minor upgrade!\r\n" ) );
1228         m_bIsMinorUpgrade = true;
1229     }
1230     else
1231     {
1232         Log( TEXT( " Didn't Find Upgrade Key in Registry -> continue with standard installation!\r\n" ) );
1233         return true;
1234     }
1235 
1236     if ( m_pProductVersion && ( _tcslen( m_pProductVersion ) > 0 ) )
1237     {
1238         TCHAR *sProductVersion = new TCHAR[ MAX_PATH + 1 ];
1239         DWORD  nSize = MAX_PATH + 1;
1240 
1241         sProductVersion[0] = '\0';
1242 
1243         // get product version
1244         if ( ERROR_SUCCESS == RegQueryValueEx( hInstKey, PRODUCT_VERSION, NULL, NULL, (LPBYTE)sProductVersion, &nSize ) )
1245         {
1246             if ( lstrcmpi( sProductVersion, m_pProductVersion ) == 0 )
1247             {
1248                 Log( TEXT( " Same Product Version already installed, no minor upgrade!\r\n" ) );
1249                 m_bIsMinorUpgrade = false;
1250             }
1251         }
1252 
1253         delete [] sProductVersion;
1254     }
1255 
1256     return true;
1257 }
1258 
1259 //--------------------------------------------------------------------------
IsTerminalServerInstalled() const1260 boolean SetupAppX::IsTerminalServerInstalled() const
1261 {
1262     boolean bIsTerminalServer = false;
1263 
1264     const TCHAR sSearchStr[]   = TEXT("Terminal Server");
1265     const TCHAR sKey[]         = TEXT("System\\CurrentControlSet\\Control\\ProductOptions");
1266     const TCHAR sValue[]       = TEXT("ProductSuite");
1267 
1268     DWORD dwSize = 0;
1269     HKEY  hKey = 0;
1270     DWORD dwType = 0;
1271 
1272     if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, sKey, 0, KEY_READ, &hKey ) &&
1273          ERROR_SUCCESS == RegQueryValueEx( hKey, sValue, NULL, &dwType, NULL, &dwSize ) &&
1274          dwSize > 0 &&
1275          REG_MULTI_SZ == dwType )
1276     {
1277         TCHAR* sSuiteList = new TCHAR[ (dwSize*sizeof(byte)/sizeof(TCHAR)) + 1 ];
1278 
1279         ZeroMemory(sSuiteList, dwSize);
1280 
1281         if ( ERROR_SUCCESS == RegQueryValueEx( hKey, sValue, NULL, &dwType, (LPBYTE)sSuiteList, &dwSize) )
1282         {
1283             DWORD nMulti = 0;
1284             DWORD nSrch  = lstrlen( sSearchStr );
1285             const TCHAR *sSubString = sSuiteList;
1286 
1287             while (*sSubString)
1288             {
1289                 nMulti = lstrlen( sSubString );
1290                 if ( nMulti == nSrch && 0 == lstrcmp( sSearchStr, sSubString ) )
1291                 {
1292                     bIsTerminalServer = true;
1293                     break;
1294                 }
1295 
1296                 sSubString += (nMulti + 1);
1297             }
1298         }
1299         delete [] sSuiteList;
1300     }
1301 
1302     if ( hKey )
1303         RegCloseKey( hKey );
1304 
1305     return bIsTerminalServer;
1306 }
1307 
1308 //--------------------------------------------------------------------------
AlreadyRunning() const1309 boolean SetupAppX::AlreadyRunning() const
1310 {
1311     if ( m_bIgnoreAlreadyRunning )
1312     {
1313         Log( TEXT("Ignoring already running MSI instance!\r\n") );
1314         return false;
1315     }
1316 
1317     const TCHAR *sMutexName    = NULL;
1318     const TCHAR sGUniqueName[] = TEXT( "Global\\_MSISETUP_{EA8130C1-8D3D-4338-9309-1A52D530D846}" );
1319     const TCHAR sUniqueName[]  = TEXT( "_MSISETUP_{EA8130C1-8D3D-4338-9309-1A52D530D846}" );
1320 
1321     if ( IsWin9x() )
1322         sMutexName = sUniqueName;
1323     else if ( ( GetOSVersion() < 5 ) && ! IsTerminalServerInstalled() )
1324         sMutexName = sUniqueName;
1325     else
1326         sMutexName = sGUniqueName;
1327 
1328     HANDLE hMutex = 0;
1329 
1330     hMutex = WIN::CreateMutex( NULL, FALSE, sMutexName );
1331 
1332     if ( !hMutex || ERROR_ALREADY_EXISTS == WIN::GetLastError() )
1333     {
1334         if ( !hMutex )
1335             Log( TEXT( "ERROR: AlreadyRunning() could not create mutex!\r\n" ) );
1336         else
1337             Log( TEXT( "ERROR: There's already a setup running!\r\n" ) );
1338 
1339         return true;
1340     }
1341     Log( TEXT( " No running Setup found\r\n" ) );
1342 
1343     return false;
1344 }
1345 
1346 //--------------------------------------------------------------------------
WaitForProcess(HANDLE hHandle)1347 DWORD SetupAppX::WaitForProcess( HANDLE hHandle )
1348 {
1349     DWORD nResult = NOERROR;
1350     boolean bLoop = true;
1351 
1352     MSG aMsg;
1353     ZeroMemory( (void*) &aMsg, sizeof(MSG) );
1354 
1355     while ( bLoop )
1356     {
1357         switch ( WIN::MsgWaitForMultipleObjects( 1, &hHandle, false,
1358                                                  INFINITE, QS_ALLINPUT ) )
1359         {
1360             case WAIT_OBJECT_0: bLoop = false;
1361                 break;
1362 
1363             case (WAIT_OBJECT_0 + 1):
1364             {
1365                 if ( WIN::PeekMessage( &aMsg, NULL, NULL, NULL, PM_REMOVE ) )
1366                 {
1367                     WIN::TranslateMessage( &aMsg );
1368                     WIN::DispatchMessage( &aMsg );
1369                 }
1370                 break;
1371             }
1372 
1373             default:
1374             {
1375                 nResult = WIN::GetLastError();
1376                 bLoop = false;
1377             }
1378         }
1379     }
1380 
1381     return nResult;
1382 }
1383 
1384 //--------------------------------------------------------------------------
Log(LPCTSTR pMessage,LPCTSTR pText) const1385 void SetupAppX::Log( LPCTSTR pMessage, LPCTSTR pText ) const
1386 {
1387     if ( m_pLogFile )
1388     {
1389         static boolean bInit = false;
1390 
1391         if ( !bInit )
1392         {
1393             bInit = true;
1394             if ( ! IsWin9x() )
1395                 _ftprintf( m_pLogFile, TEXT("%c"), 0xfeff );
1396 
1397             _tsetlocale( LC_ALL, TEXT("") );
1398             _ftprintf( m_pLogFile, TEXT("\nCodepage=%s\nMultiByte Codepage=[%d]\n"),
1399                                    _tsetlocale( LC_ALL, NULL ), _getmbcp() );
1400         }
1401         if ( pText )
1402         {
1403             _ftprintf( m_pLogFile, pMessage, pText );
1404             OutputDebugStringFormat( pMessage, pText );
1405         }
1406         else
1407         {
1408             _ftprintf( m_pLogFile, pMessage );
1409             OutputDebugStringFormat( pMessage );
1410         }
1411 
1412         fflush( m_pLogFile );
1413     }
1414 }
1415 
1416 //--------------------------------------------------------------------------
GetNextArgument(LPCTSTR pStr,LPTSTR * pArg,LPTSTR * pNext,boolean bStripQuotes)1417 DWORD SetupAppX::GetNextArgument( LPCTSTR pStr, LPTSTR *pArg, LPTSTR *pNext,
1418                                   boolean bStripQuotes )
1419 {
1420     boolean bInQuotes = false;
1421     boolean bFoundArgEnd = false;
1422     LPCTSTR pChar = pStr;
1423     LPCTSTR pFirst = NULL;
1424 
1425     if ( NULL == pChar )
1426         return ERROR_NO_MORE_ITEMS;
1427 
1428     while ( ' ' == (*pChar) || '\t' == (*pChar) )
1429         pChar = CharNext( pChar );
1430 
1431     if ( '\0' == (*pChar) )
1432         return ERROR_NO_MORE_ITEMS;
1433 
1434     int nCount = 1;
1435     pFirst = pChar;
1436 
1437     while ( ! bFoundArgEnd )
1438     {
1439         if ( '\0' == (*pChar) )
1440             bFoundArgEnd = true;
1441         else if ( !bInQuotes && ' ' == (*pChar) )
1442             bFoundArgEnd = true;
1443         else if ( !bInQuotes && '\t' == (*pChar) )
1444             bFoundArgEnd = true;
1445         else
1446         {
1447             if ( '\"' == (*pChar) )
1448             {
1449                 bInQuotes = !bInQuotes;
1450                 if ( bStripQuotes )
1451                 {
1452                     if ( pChar == pFirst )
1453                         pFirst = CharNext( pFirst );
1454                     nCount -= 1;
1455                 }
1456             }
1457 
1458             pChar = CharNext( pChar );
1459             nCount += 1;
1460         }
1461     }
1462 
1463     if ( pArg )
1464     {
1465         *pArg = new TCHAR[ nCount ];
1466         StringCchCopyN ( *pArg, nCount, pFirst, nCount-1 );
1467     }
1468 
1469     if ( pNext )
1470         *pNext = CharNext( pChar );
1471 
1472     return ERROR_SUCCESS;
1473 }
1474 
1475 //--------------------------------------------------------------------------
GetCmdLineParameters(LPTSTR * pCmdLine)1476 boolean SetupAppX::GetCmdLineParameters( LPTSTR *pCmdLine )
1477 {
1478     int    nRet   = ERROR_SUCCESS;
1479     LPTSTR pStart = NULL;
1480     LPTSTR pNext  = NULL;
1481 
1482     if ( GetNextArgument( *pCmdLine, NULL, &pNext ) != ERROR_SUCCESS )
1483     {
1484         SetError( ERROR_NO_MORE_ITEMS );
1485         return false;
1486     }
1487 
1488     int    nSize = lstrlen( *pCmdLine ) + 2;
1489     TCHAR *pNewCmdLine = new TCHAR[ nSize ];
1490     pNewCmdLine[0] = '\0';
1491 
1492     while ( GetNextArgument( pNext, &pStart, &pNext ) == ERROR_SUCCESS )
1493     {
1494         boolean bDeleteStart = true;
1495 
1496         if ( (*pStart) == '/' || (*pStart) == '-' )
1497         {
1498             LPTSTR pSub = CharNext( pStart );
1499             if ( (*pSub) == 'l' || (*pSub) == 'L' )
1500             {
1501                 pSub = CharNext( pSub );
1502                 if ( (*pSub) == 'a' || (*pSub) == 'A' )
1503                 {   // --- handle the lang parameter ---
1504                     LPTSTR pLanguage = NULL;
1505                     LPTSTR pLastChar;
1506                     if ( GetNextArgument( pNext, &pLanguage, &pNext, true ) != ERROR_SUCCESS )
1507                     {
1508                         StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pStart );
1509                         nRet = ERROR_INVALID_PARAMETER;
1510                         break;
1511                     }
1512 
1513                     m_nLanguageID = _tcstol( pLanguage, &pLastChar, 10 );
1514                     delete [] pLanguage;
1515                 }
1516                 else
1517                 {   // --- handle the l(og) parameter ---
1518                     boolean bAppend = false;
1519                     LPTSTR  pFileName = NULL;
1520 
1521                     while ( *pSub )
1522                     {
1523                         if ( *pSub == '+' )
1524                         {
1525                             bAppend = true;
1526                             break;
1527                         }
1528                         pSub = CharNext( pSub );
1529                     }
1530 
1531                     if ( GetNextArgument( pNext, &pFileName, &pNext, true ) != ERROR_SUCCESS )
1532                     {
1533                         StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pStart );
1534                         nRet = ERROR_INVALID_PARAMETER;
1535                         break;
1536                     }
1537 
1538                     if ( FAILED( StringCchCat( pNewCmdLine, nSize, pStart ) ) )
1539                     {
1540                         nRet = ERROR_OUTOFMEMORY;
1541                         break;
1542                     }
1543                     // we need to append a '+' otherwise msiexec would overwrite our log file
1544                     if ( !bAppend && FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( "+" ) ) ) )
1545                     {
1546                         nRet = ERROR_OUTOFMEMORY;
1547                         break;
1548                     }
1549                     if ( FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( " \"" ) ) ) ||
1550                         FAILED( StringCchCat( pNewCmdLine, nSize, pFileName ) ) ||
1551                         FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( "\" " ) ) ) )
1552                     {
1553                         nRet = ERROR_OUTOFMEMORY;
1554                         break;
1555                     }
1556 
1557                     if ( bAppend )
1558                         m_pLogFile = _tfopen( pFileName, TEXT( "ab" ) );
1559                     else
1560                         m_pLogFile = _tfopen( pFileName, TEXT( "wb" ) );
1561 
1562                     delete [] pFileName;
1563                 }
1564             }
1565             else if ( (*pSub) == 'q' || (*pSub) == 'Q' )
1566             {   // --- Handle quiet file parameter ---
1567                 pSub = CharNext( pSub );
1568                 if ( ! (*pSub) || (*pSub) == 'n' || (*pSub) == 'N' )
1569                     m_bQuiet = true;
1570 
1571                 if ( FAILED( StringCchCat( pNewCmdLine, nSize, pStart ) ) ||
1572                      FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( " " ) ) ) )
1573                 {
1574                     nRet = ERROR_OUTOFMEMORY;
1575                     break;
1576                 }
1577             }
1578             else if ( _tcsnicmp( pSub, PARAM_RUNNING, _tcslen( PARAM_RUNNING ) ) == 0 )
1579             {
1580                 m_bIgnoreAlreadyRunning = true;
1581             }
1582             else if ( _tcsnicmp( pSub, CMDLN_REG_ALL_MSO_TYPES, _tcslen( CMDLN_REG_ALL_MSO_TYPES ) ) == 0 )
1583             {
1584                 m_bRegAllMsoTypes = true;
1585             }
1586             else if ( _tcsnicmp( pSub, CMDLN_REG_NO_MSO_TYPES, _tcslen( CMDLN_REG_NO_MSO_TYPES ) ) == 0 )
1587             {
1588                 m_bRegNoMsoTypes = true;
1589             }
1590             else if ( (*pSub) == 'i' || (*pSub) == 'I' || (*pSub) == 'f' || (*pSub) == 'F' ||
1591                       (*pSub) == 'p' || (*pSub) == 'P' || (*pSub) == 'x' || (*pSub) == 'X' ||
1592                       (*pSub) == 'y' || (*pSub) == 'Y' || (*pSub) == 'z' || (*pSub) == 'Z' )
1593             {
1594                 StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pStart );
1595                 nRet = ERROR_INVALID_PARAMETER;
1596                 break;
1597             }
1598             else if ( (*pSub) == 'a' || (*pSub) == 'A' )
1599             {   // --- Handle Administrative Installation ---
1600                 SetAdminInstall( true );
1601             }
1602             else if ( (*pSub) == 'j' || (*pSub) == 'J' )
1603             {   // --- Handle Administrative Installation ---
1604                 m_pAdvertise = pStart;
1605                 m_bQuiet     = true;
1606                 bDeleteStart = false;
1607             }
1608             else if ( (*pSub) == '?' || (*pSub) == 'h' || (*pSub) == 'H' )
1609             {   // --- Handle Show Usage ---
1610                 nRet = ERROR_SHOW_USAGE;
1611                 break;
1612             }
1613             else
1614             {
1615                 if ( FAILED( StringCchCat( pNewCmdLine, nSize, pStart ) ) ||
1616                      FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( " " ) ) ) )
1617                 {
1618                     nRet = ERROR_OUTOFMEMORY;
1619                     break;
1620                 }
1621             }
1622         }
1623         else
1624         {
1625             if ( FAILED( StringCchCat( pNewCmdLine, nSize, pStart ) ) ||
1626                  FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( " " ) ) ) )
1627             {
1628                 nRet = ERROR_OUTOFMEMORY;
1629                 break;
1630             }
1631         }
1632 
1633         if ( bDeleteStart ) delete [] pStart;
1634         pStart = NULL;
1635     }
1636 
1637     if ( pStart ) delete [] pStart;
1638 
1639     *pCmdLine = pNewCmdLine;
1640 
1641     if ( nRet != ERROR_SUCCESS )
1642     {
1643         SetError( nRet );
1644         return false;
1645     }
1646     else
1647         return true;;
1648 }
1649 
1650 //--------------------------------------------------------------------------
IsAdmin()1651 boolean SetupAppX::IsAdmin()
1652 {
1653     if ( IsWin9x() )
1654         return true;
1655 
1656     PSID aPsidAdmin;
1657     SID_IDENTIFIER_AUTHORITY aAuthority = SECURITY_NT_AUTHORITY;
1658 
1659     if ( !AllocateAndInitializeSid( &aAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
1660                                     DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0,
1661                                     &aPsidAdmin ) )
1662         return false;
1663 
1664     BOOL bIsAdmin = FALSE;
1665 
1666     if ( GetOSVersion() >= 5 )
1667     {
1668         HMODULE hAdvapi32 = LoadLibrary( ADVAPI32_DLL );
1669 
1670         if ( !hAdvapi32 )
1671             bIsAdmin = FALSE;
1672         else
1673         {
1674             PFnCheckTokenMembership pfnCheckTokenMembership = (PFnCheckTokenMembership) GetProcAddress( hAdvapi32, ADVAPI32API_CheckTokenMembership);
1675             if ( !pfnCheckTokenMembership || !pfnCheckTokenMembership( NULL, aPsidAdmin, &bIsAdmin ) )
1676                 bIsAdmin = FALSE;
1677         }
1678         FreeLibrary( hAdvapi32 );
1679     }
1680     else
1681     {
1682         // NT4, check groups of user
1683         HANDLE hAccessToken = 0;
1684         UCHAR *szInfoBuffer = new UCHAR[ 1024 ]; // may need to resize if TokenInfo too big
1685         DWORD dwInfoBufferSize = 1024;
1686         DWORD dwRetInfoBufferSize = 0;
1687         UINT i=0;
1688 
1689         if ( WIN::OpenProcessToken( WIN::GetCurrentProcess(), TOKEN_READ, &hAccessToken ) )
1690         {
1691             bool bSuccess = false;
1692             bSuccess = WIN::GetTokenInformation( hAccessToken, TokenGroups,
1693                                                  szInfoBuffer, dwInfoBufferSize,
1694                                                  &dwRetInfoBufferSize ) == TRUE;
1695 
1696             if( dwRetInfoBufferSize > dwInfoBufferSize )
1697             {
1698                 delete [] szInfoBuffer;
1699                 szInfoBuffer = new UCHAR[ dwRetInfoBufferSize ];
1700                 dwInfoBufferSize = dwRetInfoBufferSize;
1701                 bSuccess = WIN::GetTokenInformation( hAccessToken, TokenGroups,
1702                                                      szInfoBuffer, dwInfoBufferSize,
1703                                                      &dwRetInfoBufferSize ) == TRUE;
1704             }
1705 
1706             WIN::CloseHandle( hAccessToken );
1707 
1708             if ( bSuccess )
1709             {
1710                 PTOKEN_GROUPS pGroups = (PTOKEN_GROUPS)(UCHAR*) szInfoBuffer;
1711                 for( i=0; i<pGroups->GroupCount; i++ )
1712                 {
1713                     if( WIN::EqualSid( aPsidAdmin, pGroups->Groups[i].Sid ) )
1714                     {
1715                         bIsAdmin = TRUE;
1716                         break;
1717                     }
1718                 }
1719             }
1720 
1721             delete [] szInfoBuffer;
1722         }
1723     }
1724 
1725     WIN::FreeSid( aPsidAdmin );
1726 
1727     return bIsAdmin ? true : false;
1728 }
1729 
1730 //--------------------------------------------------------------------------
CopyIniFile(LPCTSTR pIniFile)1731 LPTSTR SetupAppX::CopyIniFile( LPCTSTR pIniFile )
1732 {
1733     m_pTmpName = _ttempnam( TEXT( "C:\\" ), TEXT( "Setup" ) );
1734 
1735     if ( !m_pTmpName )
1736     {
1737         Log( TEXT( "ERROR: Could not create temp file\n" ) );
1738         return NULL;
1739     }
1740 
1741     FILE *pOut  = _tfopen( m_pTmpName, TEXT( "wb" ) );
1742     FILE *pIn   = _tfopen( pIniFile, TEXT( "rb" ) );
1743 
1744     if ( pOut && pIn )
1745     {
1746         size_t  nRead, nWritten;
1747         BYTE    pBuf[1024];
1748 
1749         nRead = fread( pBuf, sizeof( BYTE ), 1024, pIn );
1750         while ( nRead && !ferror( pIn ) )
1751         {
1752             nWritten = fwrite( pBuf, sizeof( BYTE ), nRead, pOut );
1753             if ( nWritten != nRead )
1754             {
1755                 Log( TEXT( "ERROR: Could not write all bytes to temp file\n" ) );
1756                 break;
1757             }
1758             nRead = fread( pBuf, sizeof( BYTE ), 1024, pIn );
1759         }
1760     }
1761 
1762     if ( pOut ) fclose( pOut );
1763     if ( pIn ) fclose( pIn );
1764 
1765     return m_pTmpName;
1766 }
1767 
1768 //--------------------------------------------------------------------------
ConvertNewline(LPTSTR pText) const1769 void SetupAppX::ConvertNewline( LPTSTR pText ) const
1770 {
1771     int i=0;
1772 
1773     while ( pText[i] != 0 )
1774     {
1775         if ( ( pText[i] == '\\' ) && ( pText[i+1] == 'n' ) )
1776         {
1777             pText[i] = 0x0d;
1778             pText[i+1] = 0x0a;
1779             i+=2;
1780         }
1781         else
1782             i+=1;
1783     }
1784 }
1785 
1786 //--------------------------------------------------------------------------
SetProdToAppTitle(LPCTSTR pProdName)1787 LPTSTR SetupAppX::SetProdToAppTitle( LPCTSTR pProdName )
1788 {
1789     if ( !pProdName ) return m_pAppTitle;
1790 
1791     LPTSTR pAppProdTitle = new TCHAR[ MAX_STR_CAPTION ];
1792            pAppProdTitle[0] = '\0';
1793 
1794     WIN::LoadString( m_hInst, IDS_APP_PROD_TITLE, pAppProdTitle, MAX_STR_CAPTION );
1795 
1796     int nAppLen = lstrlen( pAppProdTitle );
1797     int nProdLen = lstrlen( pProdName );
1798 
1799     if ( ( nAppLen == 0 ) || ( nProdLen == 0 ) )
1800     {
1801         delete [] pAppProdTitle;
1802         return m_pAppTitle;
1803     }
1804 
1805     int nLen = nAppLen + nProdLen + 3;
1806 
1807     if ( nLen > STRSAFE_MAX_CCH ) return m_pAppTitle;
1808 
1809     LPTSTR pIndex = _tcsstr( pAppProdTitle, PRODUCT_NAME_VAR );
1810 
1811     if ( pIndex )
1812     {
1813         int nOffset = pIndex - pAppProdTitle;
1814         int nVarLen = lstrlen( PRODUCT_NAME_VAR );
1815 
1816         LPTSTR pNewTitle = new TCHAR[ nLen ];
1817         pNewTitle[0] = '\0';
1818 
1819         if ( nOffset > 0 )
1820         {
1821             StringCchCopyN( pNewTitle, nLen, pAppProdTitle, nOffset );
1822         }
1823 
1824         StringCchCat( pNewTitle, nLen, pProdName );
1825 
1826         if ( nOffset + nVarLen < nAppLen )
1827         {
1828             StringCchCat( pNewTitle, nLen, pIndex + nVarLen );
1829         }
1830 
1831         delete [] m_pAppTitle;
1832         m_pAppTitle = pNewTitle;
1833     }
1834 
1835     delete [] pAppProdTitle;
1836 
1837     return m_pAppTitle;
1838 }
1839 
1840 
1841 //--------------------------------------------------------------------------
IsPatchInstalled(TCHAR * pBaseDir,TCHAR * pFileName)1842 boolean SetupAppX::IsPatchInstalled( TCHAR* pBaseDir, TCHAR* pFileName )
1843 {
1844     if ( !m_bSupportsPatch )
1845         return false;
1846 
1847     PMSIHANDLE hSummaryInfo;
1848     int nLen = lstrlen( pBaseDir ) + lstrlen( pFileName ) + 1;
1849     TCHAR *szDatabasePath = new TCHAR [ nLen ];
1850     TCHAR sBuf[80];
1851 
1852     StringCchCopy( szDatabasePath, nLen, pBaseDir );
1853     StringCchCat( szDatabasePath, nLen, pFileName );
1854 
1855     UINT nRet = MsiGetSummaryInformation( NULL, szDatabasePath, 0, &hSummaryInfo );
1856 
1857     if ( nRet != ERROR_SUCCESS )
1858     {
1859         StringCchPrintf( sBuf, 80, TEXT("ERROR: IsPatchInstalled: MsiGetSummaryInformation returned %u.\r\n"), nRet );
1860         Log( sBuf );
1861         return false;
1862     }
1863 
1864     UINT    uiDataType;
1865     LPTSTR  szPatchID = new TCHAR[ 64 ];
1866     DWORD   cchValueBuf = 64;
1867     nRet = MsiSummaryInfoGetProperty( hSummaryInfo, PID_REVNUMBER, &uiDataType, NULL, NULL, szPatchID, &cchValueBuf );
1868 
1869     if ( nRet != ERROR_SUCCESS )
1870     {
1871         StringCchPrintf( sBuf, 80, TEXT("ERROR: IsPatchInstalled: MsiSummaryInfoGetProperty returned %u.\r\n"), nRet );
1872         Log( sBuf );
1873         return false;
1874     }
1875 
1876 	nRet = MsiGetPatchInfo( szPatchID, INSTALLPROPERTY_LOCALPACKAGE, NULL, NULL );
1877 
1878     StringCchPrintf( sBuf, 80, TEXT("  GetPatchInfo for (%s) returned (%u)\r\n"), szPatchID, nRet );
1879     Log( sBuf );
1880 
1881 	delete []szPatchID;
1882 
1883     if ( nRet == ERROR_BAD_CONFIGURATION )
1884         return false;
1885     else if ( nRet == ERROR_INVALID_PARAMETER )
1886         return false;
1887     else if ( nRet == ERROR_MORE_DATA )
1888         return true;
1889     else if ( nRet == ERROR_SUCCESS )
1890         return true;
1891     else if ( nRet == ERROR_UNKNOWN_PRODUCT )
1892         return false;
1893     else if ( nRet == ERROR_UNKNOWN_PROPERTY )
1894         return false;
1895     else return false;
1896 
1897 	return false;
1898 }
1899 
1900 //--------------------------------------------------------------------------
InstallRuntimes(TCHAR * sProductCode,TCHAR * sRuntimePath)1901 boolean SetupAppX::InstallRuntimes( TCHAR *sProductCode, TCHAR *sRuntimePath )
1902 {
1903     INSTALLSTATE  nRet = MsiQueryProductState( sProductCode );
1904     OutputDebugStringFormat( TEXT( "MsiQueryProductState returned <%d>\r\n" ), nRet );
1905     if ( nRet == INSTALLSTATE_DEFAULT )
1906         return true;
1907 
1908     Log( TEXT( " Will install runtime <%s>\r\n" ), sRuntimePath );
1909     OutputDebugStringFormat( TEXT( " Will install runtime <%s>\r\n" ), sRuntimePath );
1910 
1911     STARTUPINFO         aSUI;
1912     PROCESS_INFORMATION aPI;
1913 
1914     ZeroMemory( (void*)&aPI, sizeof( PROCESS_INFORMATION ) );
1915     ZeroMemory( (void*)&aSUI, sizeof( STARTUPINFO ) );
1916 
1917     aSUI.cb          = sizeof(STARTUPINFO);
1918     aSUI.dwFlags     = STARTF_USESHOWWINDOW;
1919     aSUI.wShowWindow = SW_SHOW;
1920 
1921     DWORD nCmdLineLength = lstrlen( sRuntimePath ) + lstrlen( PARAM_SILENTINSTALL ) + 2;
1922     TCHAR *sCmdLine = new TCHAR[ nCmdLineLength ];
1923 
1924     if ( FAILED( StringCchCopy( sCmdLine, nCmdLineLength, sRuntimePath ) ) ||
1925          FAILED( StringCchCat(  sCmdLine, nCmdLineLength, PARAM_SILENTINSTALL ) ) )
1926     {
1927         delete [] sCmdLine;
1928         SetError( ERROR_INSTALL_FAILURE );
1929         return false;
1930     }
1931 
1932     if ( !WIN::CreateProcess( NULL, sCmdLine, NULL, NULL, FALSE,
1933                               CREATE_DEFAULT_ERROR_MODE, NULL, NULL,
1934                               &aSUI, &aPI ) )
1935     {
1936         Log( TEXT( "ERROR: Could not create process %s.\r\n" ), sCmdLine );
1937         SetError( WIN::GetLastError() );
1938         delete [] sCmdLine;
1939         return false;
1940     }
1941 
1942     DWORD nResult = WaitForProcess( aPI.hProcess );
1943     bool bRet = true;
1944 
1945     if( ERROR_SUCCESS != nResult )
1946     {
1947         Log( TEXT( "ERROR: While waiting for %s.\r\n" ), sCmdLine );
1948         SetError( nResult );
1949         bRet = false;
1950     }
1951     else
1952     {
1953         GetExitCodeProcess( aPI.hProcess, &nResult );
1954         SetError( nResult );
1955 
1956         if ( nResult != ERROR_SUCCESS )
1957         {
1958             TCHAR sBuf[80];
1959             StringCchPrintf( sBuf, 80, TEXT("Warning: install runtime returned %u.\r\n"), nResult );
1960             Log( sBuf );
1961         }
1962         else
1963             Log( TEXT( " Installation of runtime completed successfully.\r\n" ) );
1964     }
1965 
1966     CloseHandle( aPI.hProcess );
1967 
1968     delete [] sCmdLine;
1969 
1970     return bRet;
1971 }
1972 
1973 //--------------------------------------------------------------------------
InstallRuntimes()1974 boolean SetupAppX::InstallRuntimes()
1975 {
1976     TCHAR *sRuntimePath = 0;
1977     SYSTEM_INFO siSysInfo;
1978 
1979     HMODULE hKernel32 = ::LoadLibrary(_T("Kernel32.dll"));
1980     if ( hKernel32 != NULL )
1981     {
1982         typedef void (CALLBACK* pfnGetNativeSystemInfo_t)(LPSYSTEM_INFO);
1983         pfnGetNativeSystemInfo_t pfnGetNativeSystemInfo;
1984         pfnGetNativeSystemInfo = (pfnGetNativeSystemInfo_t)::GetProcAddress(hKernel32, "GetNativeSystemInfo");
1985         if ( pfnGetNativeSystemInfo != NULL )
1986         {
1987             pfnGetNativeSystemInfo(&siSysInfo);
1988         }
1989         else
1990         {
1991             // GetNativeSystemInfo does not exist. Maybe the code is running under Windows 2000.
1992             // Use GetSystemInfo instead.
1993             GetSystemInfo(&siSysInfo);
1994         }
1995         FreeLibrary(hKernel32);
1996     }
1997     else
1998     {
1999         // Failed to check Kernel32.dll. There may be something wrong.
2000         // Use GetSystemInfo instead anyway.
2001         GetSystemInfo(&siSysInfo);
2002     }
2003 
2004     OutputDebugStringFormat( TEXT( "found architecture<%d>\r\n" ), siSysInfo.wProcessorArchitecture );
2005 
2006     if ( siSysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 )
2007     {
2008         if ( GetPathToFile( RUNTIME_X64_NAME, &sRuntimePath ) )
2009             InstallRuntimes( PRODUCTCODE_X64, sRuntimePath );
2010         else
2011             Log( TEXT( "ERROR: no installer for x64 runtime libraries found!" ) );
2012 
2013         if ( sRuntimePath )
2014         {
2015             delete [] sRuntimePath;
2016             sRuntimePath = 0;
2017         }
2018     }
2019 
2020     if ( GetPathToFile( RUNTIME_X86_NAME, &sRuntimePath ) )
2021         InstallRuntimes( PRODUCTCODE_X86, sRuntimePath );
2022     else
2023         Log( TEXT( "ERROR: no installer for x86 runtime libraries found!" ) );
2024 
2025     if ( sRuntimePath )
2026         delete [] sRuntimePath;
2027 
2028     return true;
2029 }
2030 
2031 //--------------------------------------------------------------------------
2032 //--------------------------------------------------------------------------
LanguageDataX(LPTSTR pData)2033 LanguageDataX::LanguageDataX( LPTSTR pData )
2034 {
2035     m_nLanguageID = 0;
2036     m_pTransform = NULL;
2037 
2038     LPTSTR pLastChar;
2039 
2040     m_nLanguageID = _tcstol( pData, &pLastChar, 10 );
2041 
2042     if ( *pLastChar == ',' )
2043     {
2044         pLastChar += 1;
2045         int nLen = lstrlen( pLastChar ) + 1;
2046         m_pTransform = new TCHAR [ nLen ];
2047         StringCchCopy( m_pTransform, nLen, pLastChar );
2048     }
2049 }
2050 
2051 //--------------------------------------------------------------------------
~LanguageDataX()2052 LanguageDataX::~LanguageDataX()
2053 {
2054     if ( m_pTransform ) delete [] m_pTransform;
2055 }
2056 
2057 //--------------------------------------------------------------------------
2058 //--------------------------------------------------------------------------
Create_SetupAppX()2059 SetupApp* Create_SetupAppX()
2060 {
2061     return new SetupAppX;
2062 }
2063 
2064 //--------------------------------------------------------------------------
2065