1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #define _WIN32_WINDOWS 0x0410
29 
30 #ifdef _MSC_VER
31 #pragma warning(push, 1) /* disable warnings within system headers */
32 #endif
33 #define WIN32_LEAN_AND_MEAN
34 #include <windows.h>
35 #include <msiquery.h>
36 #ifdef _MSC_VER
37 #pragma warning(pop)
38 #endif
39 
40 #include <malloc.h>
41 #include <assert.h>
42 
43 #ifdef UNICODE
44 #define _UNICODE
45 #define _tstring	wstring
46 #else
47 #define _tstring	string
48 #endif
49 #include <tchar.h>
50 #include <string>
51 #include <queue>
52 #include <stdio.h>
53 
54 #include <systools/win32/uwinapi.h>
55 #include <../tools/seterror.hxx>
56 
57 #define	WININIT_FILENAME	"wininit.ini"
58 #define RENAME_SECTION		"rename"
59 
60 #ifdef DEBUG
61 inline void OutputDebugStringFormat( LPCTSTR pFormat, ... )
62 {
63 	_TCHAR	buffer[1024];
64 	va_list	args;
65 
66 	va_start( args, pFormat );
67 	_vsntprintf( buffer, elementsof(buffer), pFormat, args );
68 	OutputDebugString( buffer );
69 }
70 #else
71 static inline void OutputDebugStringFormat( LPCTSTR, ... )
72 {
73 }
74 #endif
75 
76 static std::_tstring GetMsiProperty( MSIHANDLE handle, const std::_tstring& sProperty )
77 {
78 	std::_tstring	result;
79 	TCHAR	szDummy[1] = TEXT("");
80 	DWORD	nChars = 0;
81 
82 	if ( MsiGetProperty( handle, sProperty.c_str(), szDummy, &nChars ) == ERROR_MORE_DATA )
83 	{
84 		DWORD nBytes = ++nChars * sizeof(TCHAR);
85 		LPTSTR buffer = reinterpret_cast<LPTSTR>(_alloca(nBytes));
86 		ZeroMemory( buffer, nBytes );
87 		MsiGetProperty(handle, sProperty.c_str(), buffer, &nChars);
88 		result = buffer;
89 	}
90 
91 	return	result;
92 }
93 
94 // The provided GUID must be without surounding '{}'
95 static std::_tstring GetGuidPart(const std::_tstring& guid, int index)
96 {
97 	assert((guid.length() == 36) && "No GUID or wrong format!");
98 	assert(((index > -1) && (index < 5)) && "Out of range!");
99 
100 	if (index == 0) return std::_tstring(guid.c_str(), 8);
101 	if (index == 1) return std::_tstring(guid.c_str() + 9, 4);
102 	if (index == 2) return std::_tstring(guid.c_str() + 14, 4);
103 	if (index == 3) return std::_tstring(guid.c_str() + 19, 4);
104 	if (index == 4) return std::_tstring(guid.c_str() + 24, 12);
105 
106 	return std::_tstring();
107 }
108 
109 static void Swap(char* p1, char* p2)
110 {
111 	char tmp = *p1;
112 	*p1 = *p2;
113 	*p2 = tmp;
114 }
115 
116 static std::_tstring Invert(const std::_tstring& str)
117 {
118 	char* buff = reinterpret_cast<char*>(_alloca(str.length()));
119 	strncpy(buff, str.c_str(), str.length());
120 
121 	char* front = buff;
122 	char* back = buff + str.length() - 1;
123 
124 	while (front < back)
125 		Swap(front++, back--);
126 
127 	return std::_tstring(buff, str.length());
128 }
129 
130 // Convert the upgrade code (which is a GUID) according
131 // to the way the windows installer does when writing it
132 // to the registry
133 // The first 8 bytes will be inverted, from the the last
134 // 8 bytes always the nibbles will be inverted for further
135 // details look in the MSDN under compressed registry keys
136 static std::_tstring ConvertGuid(const std::_tstring& guid)
137 {
138 	std::_tstring convertedGuid;
139 
140 	std::_tstring part = GetGuidPart(guid, 0);
141 	convertedGuid = Invert(part);
142 
143 	part = GetGuidPart(guid, 1);
144 	convertedGuid += Invert(part);
145 
146 	part = GetGuidPart(guid, 2);
147 	convertedGuid += Invert(part);
148 
149 	part = GetGuidPart(guid, 3);
150 	convertedGuid += Invert(std::_tstring(part.c_str(), 2));
151 	convertedGuid += Invert(std::_tstring(part.c_str() + 2, 2));
152 
153 	part = GetGuidPart(guid, 4);
154 	int pos = 0;
155 	for (int i = 0; i < 6; i++)
156 	{
157 		convertedGuid += Invert(std::_tstring(part.c_str() + pos, 2));
158 		pos += 2;
159 	}
160 	return convertedGuid;
161 }
162 
163 static inline bool IsSetMsiProperty(MSIHANDLE handle, const std::_tstring& sProperty)
164 {
165 	std::_tstring value = GetMsiProperty(handle, sProperty);
166 	return (value.length() > 0);
167 }
168 
169 static inline void UnsetMsiProperty(MSIHANDLE handle, const std::_tstring& sProperty)
170 {
171 	MsiSetProperty(handle, sProperty.c_str(), NULL);
172 }
173 
174 static inline void SetMsiProperty(MSIHANDLE handle, const std::_tstring& sProperty)
175 {
176 	MsiSetProperty(handle, sProperty.c_str(), TEXT("1"));
177 }
178 
179 static BOOL MoveFileEx9x( LPCSTR lpExistingFileNameA, LPCSTR lpNewFileNameA, DWORD dwFlags )
180 {
181 	BOOL	fSuccess = FALSE;	// assume failure
182 
183 	// Windows 9x has a special mechanism to move files after reboot
184 
185 	if ( dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT )
186 	{
187 		CHAR	szExistingFileNameA[MAX_PATH];
188 		CHAR	szNewFileNameA[MAX_PATH] = "NUL";
189 
190 		// Path names in WININIT.INI must be in short path name form
191 
192 		if (
193 			GetShortPathNameA( lpExistingFileNameA, szExistingFileNameA, MAX_PATH ) &&
194 			(!lpNewFileNameA || GetShortPathNameA( lpNewFileNameA, szNewFileNameA, MAX_PATH ))
195 			)
196 		{
197 			CHAR	szBuffer[32767];	// The buffer size must not exceed 32K
198 			DWORD	dwBufLen = GetPrivateProfileSectionA( RENAME_SECTION, szBuffer, elementsof(szBuffer), WININIT_FILENAME );
199 
200 			CHAR	szRename[MAX_PATH];	// This is enough for at most to times 67 chracters
201 			strcpy( szRename, szNewFileNameA );
202 			strcat( szRename, "=" );
203 			strcat( szRename, szExistingFileNameA );
204 			size_t	lnRename = strlen(szRename);
205 
206 			if ( dwBufLen + lnRename + 2 <= elementsof(szBuffer) )
207 			{
208 				CopyMemory( &szBuffer[dwBufLen], szRename, lnRename );
209 				szBuffer[dwBufLen + lnRename ] = 0;
210 				szBuffer[dwBufLen + lnRename + 1 ] = 0;
211 
212 				fSuccess = WritePrivateProfileSectionA( RENAME_SECTION, szBuffer, WININIT_FILENAME );
213 			}
214 			else
215 				SetLastError( ERROR_BUFFER_OVERFLOW );
216 		}
217 	}
218 	else
219 	{
220 
221 		fSuccess = MoveFileA( lpExistingFileNameA, lpNewFileNameA );
222 
223 		if ( !fSuccess && GetLastError() != ERROR_ACCESS_DENIED &&
224 			0 != (dwFlags & (MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) )
225 		{
226 			BOOL	bFailIfExist = 0 == (dwFlags & MOVEFILE_REPLACE_EXISTING);
227 
228 			fSuccess = CopyFileA( lpExistingFileNameA, lpNewFileNameA, bFailIfExist );
229 
230 			if ( fSuccess )
231 				fSuccess = DeleteFileA( lpExistingFileNameA );
232 		}
233 
234 	}
235 
236 	return fSuccess;
237 }
238 
239 static BOOL MoveFileExImpl( LPCSTR lpExistingFileNameA, LPCSTR lpNewFileNameA, DWORD dwFlags )
240 {
241 	if ( 0 > ((LONG)GetVersion())) // High order bit indicates Win 9x
242 		return MoveFileEx9x( lpExistingFileNameA, lpNewFileNameA, dwFlags );
243 	else
244 		return MoveFileExA( lpExistingFileNameA, lpNewFileNameA, dwFlags );
245 }
246 
247 static bool SwapFiles( const std::_tstring& sFileName1, const std::_tstring& sFileName2 )
248 {
249 	std::_tstring	sTempFileName = sFileName1 + TEXT(".tmp");
250 
251 	bool fSuccess = true;
252 
253 	//Try to move the original file to a temp file
254 	fSuccess = MoveFileExImpl( sFileName1.c_str(), sTempFileName.c_str(), MOVEFILE_REPLACE_EXISTING);
255 
256 	std::_tstring	mystr;
257 
258 	if ( fSuccess )
259 	{
260 		fSuccess = MoveFileExImpl( sFileName2.c_str(), sFileName1.c_str(), MOVEFILE_REPLACE_EXISTING );
261 
262 		if ( fSuccess )
263 		{
264 			fSuccess = MoveFileExImpl( sTempFileName.c_str(), sFileName2.c_str(),
265 										MOVEFILE_REPLACE_EXISTING );
266 			if ( !fSuccess )
267 			{
268 				MoveFileExImpl( sFileName1.c_str(), sFileName2.c_str(), MOVEFILE_REPLACE_EXISTING );
269 			}
270 		}
271 		else
272 		{
273 			MoveFileExImpl( sTempFileName.c_str(), sFileName1.c_str(), MOVEFILE_REPLACE_EXISTING  );
274 		}
275 	}
276 	else
277 	{
278 		//It could be that there is no original file and therefore copying the original to a temp
279 		// file failed. Examine if there is no original and if so then move file2 to file1
280 
281 		WIN32_FIND_DATA data;
282 		HANDLE hdl = FindFirstFile(sFileName1.c_str(), &data);
283 		if (hdl == INVALID_HANDLE_VALUE)
284 		{
285 			fSuccess = MoveFileExImpl( sFileName2.c_str(), sFileName1.c_str(), MOVEFILE_REPLACE_EXISTING );
286 
287 			// if ( fSuccess )
288 			// {
289 			//	mystr = "Success";
290 			//	MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
291 			// }
292 			// else
293 			// {
294 			//	char buff[256];
295 			//	wsprintf(buff, "Failure %d", GetLastError());
296 			//	MessageBox( NULL, buff, "Titel", MB_OK );
297 			// }
298 		}
299 		else
300 		{
301 			FindClose(hdl);
302 		}
303 	}
304 
305 	OutputDebugStringFormat( TEXT("%s <-> %s: %s"), sFileName1.c_str(), sFileName2.c_str(), fSuccess ? TEXT("OK") : TEXT("FAILED") );
306 
307 	if (!fSuccess )
308 	{
309 		DWORD	dwError = GetLastError();
310 		LPVOID lpMsgBuf;
311 		if ( FormatMessage(
312 			FORMAT_MESSAGE_ALLOCATE_BUFFER |
313 			FORMAT_MESSAGE_FROM_SYSTEM |
314 			FORMAT_MESSAGE_IGNORE_INSERTS,
315 			NULL,
316 			GetLastError(),
317 			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
318 			(LPTSTR) &lpMsgBuf,
319 			0,
320 			NULL ))
321 		{
322 			OutputDebugStringFormat( TEXT("Error Code %d: %s"), dwError, lpMsgBuf );
323 			LocalFree( lpMsgBuf );
324 		}
325 		else
326 			OutputDebugStringFormat( TEXT("Error Code %d: Unknown"), dwError );
327         SetMsiErrorCode( dwError );
328 	}
329 
330 	return fSuccess;
331 }
332 
333 static std::_tstring strip( const std::_tstring& s, _TCHAR c )
334 {
335 	std::_tstring	result = s;
336 
337 	std::_tstring::size_type f;
338 
339 	do
340 	{
341 		f = result.find( c );
342 		if ( f != std::_tstring::npos )
343 			result.erase( f, 1 );
344 	} while ( f != std::_tstring::npos );
345 
346 	return result;
347 }
348 
349 static std::_tstring trim( const std::_tstring& rString )
350 {
351 	std::_tstring temp = rString;
352 
353 	while ( temp.length() && temp[0] == ' ' || temp[0] == '\t' )
354 		temp.erase( 0, 1 );
355 
356 	std::_tstring::size_type	len = temp.length();
357 
358 	while ( len && temp[len-1] == ' ' || temp[len-1] == '\t' )
359 	{
360 		temp.erase( len - 1, 1 );
361 		len = temp.length();
362 	}
363 
364 	return temp;
365 }
366 
367 static bool readLine( FILE *fp, std::_tstring& rLine )
368 {
369 	_TCHAR szBuffer[1024];
370 	bool	bSuccess = false;
371 	bool	bEOL = false;
372 	std::_tstring	line;
373 
374 
375 	while ( !bEOL && _fgetts( szBuffer, sizeof(szBuffer), fp ) )
376 	{
377 		int	len = _tcslen(szBuffer);
378 
379 		bSuccess = true;
380 
381 		while ( len && szBuffer[len - 1] == '\n' )
382 		{
383 			szBuffer[--len] = 0;
384 			bEOL = true;
385 		}
386 
387 		line.append( szBuffer );
388 	}
389 
390 	rLine = line;
391 	return bSuccess;
392 }
393 
394 
395 static std::_tstring getProfileString(
396 	const std::_tstring& aFileName,
397 	const std::_tstring& aSectionName,
398 	const std::_tstring& aKeyName,
399 	const std::_tstring& aDefault = _T("") )
400 {
401 	FILE	*fp = _tfopen( aFileName.c_str(), _T("r") );
402 	std::_tstring	retValue = aDefault.length() ? aDefault : _T("");
403 
404 	if ( fp )
405 	{
406 		std::_tstring line;
407 		std::_tstring section;
408 
409 		while ( readLine( fp, line ) )
410 		{
411 			line = trim( line );
412 
413 			if ( line.length() && line[0] == '[' )
414 			{
415 				line.erase( 0, 1 );
416 				std::_tstring::size_type end = line.find( ']', 0 );
417 
418 				if ( std::_tstring::npos != end )
419 					section = trim( line.substr( 0, end ) );
420 			}
421 			else
422 			{
423 
424 				std::_tstring::size_type iEqualSign = line.find( '=', 0 );
425 
426 				if ( iEqualSign != std::_tstring::npos )
427 				{
428 					std::_tstring	keyname = line.substr( 0, iEqualSign );
429 					keyname = trim( keyname );
430 
431 					std::_tstring	value = line.substr( iEqualSign + 1 /*, std::_tstring::npos */ );
432 					value = trim( value );
433 
434 					if (
435 						0 == _tcsicmp( section.c_str(), aSectionName.c_str() ) &&
436 						0 == _tcsicmp( keyname.c_str(), aKeyName.c_str() )
437 						 )
438 					{
439 						retValue = value;
440 						break;
441 					}
442 				}
443 			}
444 		}
445 
446 		fclose( fp );
447 	}
448 
449 	return retValue;
450 }
451 
452 static std::queue< std::_tstring > getProfileSections( const std::_tstring& aFileName )
453 {
454 	FILE	*fp = _tfopen( aFileName.c_str(), _T("r") );
455 	std::queue< std::_tstring >	aResult;
456 
457 	OutputDebugStringFormat( TEXT("*** Retrieving Section Names ****") );
458 
459 	if ( fp )
460 	{
461 		std::_tstring line;
462 		std::_tstring section;
463 
464 		while ( readLine( fp, line ) )
465 		{
466 			line = trim( line );
467 
468 			if ( line.length() && line[0] == '[' )
469 			{
470 				line.erase( 0, 1 );
471 				std::_tstring::size_type end = line.find( ']', 0 );
472 
473 				if ( std::_tstring::npos != end )
474 					section = trim( line.substr( 0, end ) );
475 
476 				aResult.push( section );
477 
478 				OutputDebugStringFormat( TEXT("Section: %s"), section.c_str() );
479 
480 			}
481 		}
482 
483 		fclose( fp );
484 	}
485 
486 	OutputDebugStringFormat( TEXT("*** Done Section Names ***") );
487 
488 	return aResult;
489 }
490 
491 static std::queue< std::_tstring > getProfileKeys( const std::_tstring& aFileName, const std::_tstring& aSectionName )
492 {
493 	FILE	*fp = _tfopen( aFileName.c_str(), _T("r") );
494 	std::queue< std::_tstring >	aResult;
495 
496 	OutputDebugStringFormat( TEXT("*** Retrieving Key Names for [%s] ***"), aSectionName.c_str() );
497 
498 	if ( fp )
499 	{
500 		std::_tstring line;
501 		std::_tstring section;
502 
503 		while ( readLine( fp, line ) )
504 		{
505 			line = trim( line );
506 
507 			if ( line.length() && line[0] == '[' )
508 			{
509 				line.erase( 0, 1 );
510 				std::_tstring::size_type end = line.find( ']', 0 );
511 
512 				if ( std::_tstring::npos != end )
513 					section = trim( line.substr( 0, end ) );
514 			}
515 			else
516 			{
517 
518 				std::_tstring::size_type iEqualSign = line.find( '=', 0 );
519 
520 				if ( iEqualSign != std::_tstring::npos )
521 				{
522 					std::_tstring	keyname = line.substr( 0, iEqualSign );
523 					keyname = trim( keyname );
524 
525 					if ( 0 == _tcsicmp( section.c_str(), aSectionName.c_str() ) )
526 					{
527 						aResult.push( keyname );
528 
529 						OutputDebugStringFormat( keyname.c_str() );
530 
531 					}
532 				}
533 			}
534 		}
535 
536 		fclose( fp );
537 	}
538 
539 	OutputDebugStringFormat( TEXT("*** Done Key Names for [%s] ***"), aSectionName.c_str() );
540 
541 	return aResult;
542 }
543 
544 extern "C" UINT __stdcall InstallPatchedFiles( MSIHANDLE handle )
545 {
546 	std::_tstring	sInstDir = GetMsiProperty( handle, TEXT("INSTALLLOCATION") );
547 	std::_tstring	sProgramDir = sInstDir + TEXT("Basis\\program\\");
548 	std::_tstring	sPatchFile = sProgramDir + TEXT("patchlist.txt");
549 
550 	std::queue< std::_tstring > aSectionNames;
551 	std::queue< std::_tstring > aKeyNames;
552 
553 	OutputDebugStringA( "Starting Custom Action" );
554 
555 	// std::_tstring	mystr;
556 	// mystr = "Patchfile: " + sPatchFile;
557 	// MessageBox( NULL, mystr.c_str(), "Patchfile", MB_OK );
558 
559 	aSectionNames = getProfileSections( sPatchFile );
560 	while ( !aSectionNames.empty() )
561 	{
562 		std::_tstring	sSectionName = aSectionNames.front();
563 		if ( std::_tstring(TEXT("_root")) == sSectionName ) { sSectionName = TEXT(""); }
564 		// mystr = "Section: " + sSectionName;
565 		// MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
566 
567 		aKeyNames = getProfileKeys( sPatchFile, sSectionName );
568 		while ( !aKeyNames.empty() )
569 		{
570 			std::_tstring	sKeyName = aKeyNames.front();
571 			std::_tstring	sValue = getProfileString( sPatchFile, sSectionName, sKeyName );
572 
573 			if ( sValue.length() )
574 			{
575 				std::_tstring	sFileName1 = sKeyName;
576 				std::_tstring	sExtension = sValue;
577 				std::_tstring	sFileName2;
578 
579 				sFileName1 = strip( sFileName1, '\"' );
580 				sExtension = strip( sExtension, '\"' );
581 
582 				sFileName1 = sInstDir + sSectionName + sFileName1;
583 				sFileName2 = sFileName1 + sExtension;
584 
585 				// mystr = "Convert: " + sFileName1 + " to " + sFileName2;
586 				// MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
587 
588 				SwapFiles( sFileName1, sFileName2 );
589 			}
590 
591 			aKeyNames.pop();
592 		}
593 
594 		aSectionNames.pop();
595 	}
596 
597 	return ERROR_SUCCESS;
598 }
599 
600 extern "C" UINT __stdcall UninstallPatchedFiles( MSIHANDLE handle )
601 {
602 	TCHAR	szValue[8192];
603 	DWORD	nValueSize = sizeof(szValue);
604 	HKEY	hKey;
605 
606 	std::_tstring	sInstDir;
607 
608 	std::_tstring	sProductKey = GetMsiProperty( handle, TEXT("FINDPRODUCT") );
609 
610 	if ( ERROR_SUCCESS == RegOpenKey( HKEY_CURRENT_USER,  sProductKey.c_str(), &hKey ) )
611 	{
612 		if ( ERROR_SUCCESS == RegQueryValueEx( hKey, TEXT("INSTALLLOCATION"), NULL, NULL, (LPBYTE)szValue, &nValueSize ) )
613 		{
614 			sInstDir = szValue;
615 		}
616 		RegCloseKey( hKey );
617 	}
618 	else if ( ERROR_SUCCESS == RegOpenKey( HKEY_LOCAL_MACHINE,  sProductKey.c_str(), &hKey ) )
619 	{
620 		if ( ERROR_SUCCESS == RegQueryValueEx( hKey, TEXT("INSTALLLOCATION"), NULL, NULL, (LPBYTE)szValue, &nValueSize ) )
621 		{
622 			sInstDir = szValue;
623 		}
624 		RegCloseKey( hKey );
625 	}
626 	else
627 		return ERROR_SUCCESS;
628 
629 	std::_tstring	sProgramDir = sInstDir + TEXT("Basis\\program\\");
630 	std::_tstring	sPatchFile = sProgramDir + TEXT("patchlist.txt");
631 
632 	std::queue< std::_tstring >	aSectionNames;
633 	std::queue< std::_tstring >	aKeyNames;
634 
635 	// std::_tstring	mystr;
636 	// mystr = "Patchfile: " + sPatchFile;
637 	// MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
638 
639 	aSectionNames = getProfileSections( sPatchFile );
640 	while ( !aSectionNames.empty() )
641 	{
642 		std::_tstring	sSectionName = aSectionNames.front();
643 		if ( std::_tstring(TEXT("_root")) == sSectionName ) { sSectionName = TEXT(""); }
644 		// mystr = "Section: " + sSectionName;
645 		// MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
646 
647 		aKeyNames = getProfileKeys( sPatchFile, sSectionName );
648 		while( !aKeyNames.empty() )
649 		{
650 			std::_tstring	sKeyName = aKeyNames.front();
651 			std::_tstring	sValue = getProfileString( sPatchFile, sSectionName, sKeyName );
652 
653 			if ( sValue.length() )
654 			{
655 				std::_tstring	sFileName1 = sKeyName;
656 				std::_tstring	sExtension = sValue;
657 				std::_tstring	sFileName2;
658 
659 				sFileName1 = strip( sFileName1, '\"' );
660 				sExtension = strip( sExtension, '\"' );
661 
662 				sFileName1 = sInstDir + sSectionName + sFileName1;
663 				sFileName2 = sFileName1 + sExtension;
664 
665 				// mystr = "Convert: " + sFileName1 + " to " + sFileName2;
666 				// MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
667 
668 				SwapFiles( sFileName2, sFileName1 );
669 			}
670 
671 			aKeyNames.pop();
672 		}
673 
674 		aSectionNames.pop();
675 	}
676 
677 	return ERROR_SUCCESS;
678 }
679 
680 extern "C" UINT __stdcall IsOfficeRunning( MSIHANDLE handle )
681 {
682 	std::_tstring	sInstDir = GetMsiProperty( handle, TEXT("INSTALLLOCATION") );
683 	std::_tstring	sResourceDir = sInstDir + TEXT("Basis\\program\\resource\\");
684 	std::_tstring	sPattern = sResourceDir + TEXT("vcl*.res");
685 
686 	WIN32_FIND_DATA	aFindFileData;
687 	HANDLE	hFind = FindFirstFile( sPattern.c_str(), &aFindFileData );
688 
689 	if ( IsValidHandle(hFind) )
690 	{
691 		BOOL	fSuccess = false;
692 		bool	fRenameSucceeded;
693 
694 		do
695 		{
696 			std::_tstring	sResourceFile = sResourceDir + aFindFileData.cFileName;
697 			std::_tstring	sIntermediate = sResourceFile + TEXT(".tmp");
698 
699 			fRenameSucceeded = MoveFileExImpl( sResourceFile.c_str(), sIntermediate.c_str(), MOVEFILE_REPLACE_EXISTING );
700 			if ( fRenameSucceeded )
701 			{
702 				MoveFileExImpl( sIntermediate.c_str(), sResourceFile.c_str(), 0 );
703 				fSuccess = FindNextFile( hFind, &aFindFileData );
704 			}
705 		} while ( fSuccess && fRenameSucceeded );
706 
707 		if ( !fRenameSucceeded )
708         {
709 			MsiSetProperty(handle, TEXT("OFFICERUNS"), TEXT("1"));
710             SetMsiErrorCode( MSI_ERROR_OFFICE_IS_RUNNING );
711         }
712 
713 		FindClose( hFind );
714 	}
715 
716 
717 	return ERROR_SUCCESS;
718 }
719 
720 extern "C" UINT __stdcall SetFeatureState( MSIHANDLE handle )
721 {
722 	std::_tstring	mystr;
723 
724 	// 1. Reading Product Code from setup.ini of installed Office
725 
726 	std::_tstring sInstallPath = GetMsiProperty(handle, TEXT("INSTALLLOCATION"));
727 	// MessageBox(NULL, sInstallPath.c_str(), "INSTALLLOCATION", MB_OK);
728 	std::_tstring sSetupiniPath = sInstallPath + TEXT("program\\setup.ini");
729 
730 	TCHAR szProductCode[32767];
731 
732 	GetPrivateProfileString(
733 		TEXT("Bootstrap"),
734 		TEXT("ProductCode"),
735 		TEXT("NOTFOUND"),
736 		szProductCode,
737 		elementsof(szProductCode),
738 		sSetupiniPath.c_str()
739 		);
740 
741 	if ( !_tcsicmp( szProductCode, TEXT("NOTFOUND") ) )
742 	{
743 		// No setup.ini or no "ProductCode" in setup.ini. This is an invalid directory.
744 		// MessageBox(NULL, "NOTFOUND set", "DEBUG", MB_OK);
745 		return ERROR_SUCCESS;
746 	}
747 
748 	// 2. Converting Product code
749 
750 	std::_tstring productCode = TEXT(szProductCode);
751 	productCode = ConvertGuid(std::_tstring(productCode.c_str() + 1, productCode.length() - 2));
752 	mystr = TEXT("Changed product code: ") + productCode;
753 	// MessageBox(NULL, mystr.c_str(), "ProductCode", MB_OK);
754 
755 	// 3. Setting path in the Windows registry to find installed features
756 
757 	std::_tstring registryKey;
758 	HKEY registryRoot;
759 
760 	if ( IsSetMsiProperty(handle, TEXT("ALLUSERS")) )
761 	{
762 		registryRoot = HKEY_LOCAL_MACHINE;
763 		registryKey = TEXT("Software\\Classes\\Installer\\Features\\") + productCode;
764 		mystr = registryKey;
765 		// MessageBox( NULL, mystr.c_str(), "ALLUSERS", MB_OK );
766 	}
767 	else
768 	{
769 		registryRoot = HKEY_CURRENT_USER;
770 		registryKey = TEXT("Software\\Microsoft\\Installer\\Features\\") + productCode;
771 		mystr = registryKey;
772 		// MessageBox( NULL, mystr.c_str(), "ALLUSERS", MB_OK );
773 	}
774 
775 	// 4. Collecting all installed features from Windows registry
776 
777 	HKEY hKey;
778 	if (RegOpenKey(registryRoot, registryKey.c_str(), &hKey) == ERROR_SUCCESS)
779 	{
780 		int counter = 0;
781 		// DWORD counter = 0;
782 		LONG lEnumResult;
783 
784 		do
785 		{
786 			TCHAR szValueName[8192];
787 			DWORD nValueNameSize = sizeof(szValueName);
788 			LPDWORD pValueNameSize = &nValueNameSize;
789 			TCHAR szValueData[8192];
790 			DWORD nValueDataSize = sizeof(szValueData);
791 
792 			lEnumResult = RegEnumValue( hKey, counter, szValueName, pValueNameSize, NULL, NULL, (LPBYTE)szValueData, &nValueDataSize);
793 
794 			if ( ERROR_SUCCESS == lEnumResult )
795 			{
796 				std::_tstring sValueName = szValueName;
797 				std::_tstring sValueData = szValueData;
798 
799 				// mystr = sValueName;
800 				// MessageBox( NULL, mystr.c_str(), "ValueName", MB_OK );
801 				// mystr = sValueData;
802 				// MessageBox( NULL, mystr.c_str(), "ValueData", MB_OK );
803 
804 				// Does this feature exist in this patch?
805 				if ( IsSetMsiProperty(handle, sValueName) )
806 				{
807 					// Feature is not installed, if szValueData starts with a "square" (ascii 6)
808 					if ( 6 == szValueData[0] )
809 					{
810 						MsiSetFeatureState(handle,sValueName.c_str(),INSTALLSTATE_ABSENT); // do not install this feature
811 						// mystr = TEXT("Do NOT install: ") + sValueName;
812 						// MessageBox( NULL, mystr.c_str(), "ValueName", MB_OK );
813 					}
814 					else
815 					{
816 						MsiSetFeatureState(handle,sValueName.c_str(),INSTALLSTATE_LOCAL); // do install this feature
817 						// mystr = TEXT("Do install: ") + sValueName;
818 						// MessageBox( NULL, mystr.c_str(), "ValueName", MB_OK );
819 					}
820 				}
821 			}
822 
823 			counter = counter + 1;
824 
825 		} while ( ERROR_SUCCESS == lEnumResult );
826 
827 		RegCloseKey( hKey );
828 	}
829 
830 	return ERROR_SUCCESS;
831 }
832 
833 extern "C" UINT __stdcall SetNewFeatureState( MSIHANDLE handle )
834 {
835 	std::_tstring mystr;
836     std::_tstring sValueName;
837 
838     sValueName = TEXT("gm_o_Onlineupdate");
839 
840     if (IsSetMsiProperty(handle, TEXT("SELECT_OU_FEATURE")))
841     {
842         MsiSetFeatureState(handle,sValueName.c_str(),INSTALLSTATE_LOCAL); // do install this feature
843         // mystr = TEXT("OnlineUpdate wird installiert!");
844         // MessageBox(NULL, mystr.c_str(), "INSTALLSTATE_LOCAL", MB_OK);
845     }
846     else
847     {
848         MsiSetFeatureState(handle,sValueName.c_str(),INSTALLSTATE_ABSENT); // do not install this feature
849         // mystr = TEXT("OnlineUpdate wird NICHT installiert!");
850         // MessageBox(NULL, mystr.c_str(), "INSTALLSTATE_ABSENT", MB_OK);
851     }
852 
853 	return ERROR_SUCCESS;
854 }
855 
856 extern "C" UINT __stdcall ShowOnlineUpdateDialog( MSIHANDLE handle )
857 {
858     // Checking existence of file "updchk.uno.dll", which shows, that
859     // Online Update functionality is always available. Then the dialog
860     // that offers the Online Update is superfluous.
861 
862     std::_tstring sInstDir = GetMsiProperty( handle, TEXT("INSTALLLOCATION") );
863     std::_tstring sProgramDir = sInstDir + TEXT("Basis\\program\\");
864     std::_tstring sSearchFile = sProgramDir + TEXT("updchk.uno.dll");
865 
866     WIN32_FIND_DATA data;
867     HANDLE hdl = FindFirstFile(sSearchFile.c_str(), &data);
868     if (hdl != INVALID_HANDLE_VALUE)  // the file exists
869     {
870         // std::_tstring mystr;
871         // mystr = "Found file: " + sSearchFile;
872         // MessageBox( NULL, mystr.c_str(), "Found file", MB_OK );
873 
874         // And finally setting property SHOW_ONLINEUPDATE_DIALOG
875         // to hide this dialog
876         UnsetMsiProperty(handle, TEXT("SHOW_ONLINEUPDATE_DIALOG"));
877 
878         // Setting SELECT_OU_FEATURE to 1, which is probably superfluous
879         // because this is already the default value. But only this
880         // guarantees, that CustomAction SetNewFeatureState always sets
881         // the correct FeatureState for "gm_o_Onlineupdate", if it is
882         // already installed.
883         SetMsiProperty(handle, TEXT("SELECT_OU_FEATURE"));
884     }
885     else
886     {
887         // std::_tstring mystr;
888         // mystr = "Did not find file: " + sSearchFile;
889         // MessageBox( NULL, mystr.c_str(), "File not found", MB_OK );
890 
891         // If the file does not exist, the Online Update dialog
892         // has to be shown.
893         SetMsiProperty(handle, TEXT("SHOW_ONLINEUPDATE_DIALOG"));
894         FindClose(hdl);
895     }
896 
897 	return ERROR_SUCCESS;
898 }
899