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