Version 3.6.0.4, tag libreoffice-3.6.0.4
[LibreOffice.git] / setup_native / source / win32 / customactions / patch / swappatchfiles.cxx
blob7f83ca57b29ff36bdb79a12a5d605bc532bddf32
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
29 #define _WIN32_WINDOWS 0x0410
31 #ifdef _MSC_VER
32 #pragma warning(push, 1) /* disable warnings within system headers */
33 #endif
34 #define WIN32_LEAN_AND_MEAN
35 #include <windows.h>
36 #include <msiquery.h>
37 #ifdef _MSC_VER
38 #pragma warning(pop)
39 #endif
41 #include <malloc.h>
42 #include <assert.h>
44 #ifdef UNICODE
45 #define _UNICODE
46 #define _tstring wstring
47 #else
48 #define _tstring string
49 #endif
50 #include <tchar.h>
51 #include <string>
52 #include <queue>
53 #include <stdio.h>
54 #include <sal/macros.h>
56 #include <systools/win32/uwinapi.h>
57 #include <../tools/seterror.hxx>
59 #ifdef DEBUG
60 inline void OutputDebugStringFormat( LPCTSTR pFormat, ... )
62 _TCHAR buffer[1024];
63 va_list args;
65 va_start( args, pFormat );
66 _vsntprintf( buffer, SAL_N_ELEMENTS(buffer), pFormat, args );
67 OutputDebugString( buffer );
69 #else
70 static inline void OutputDebugStringFormat( LPCTSTR, ... )
73 #endif
75 static std::_tstring GetMsiProperty( MSIHANDLE handle, const std::_tstring& sProperty )
77 std::_tstring result;
78 TCHAR szDummy[1] = TEXT("");
79 DWORD nChars = 0;
81 if ( MsiGetProperty( handle, sProperty.c_str(), szDummy, &nChars ) == ERROR_MORE_DATA )
83 DWORD nBytes = ++nChars * sizeof(TCHAR);
84 LPTSTR buffer = reinterpret_cast<LPTSTR>(_alloca(nBytes));
85 ZeroMemory( buffer, nBytes );
86 MsiGetProperty(handle, sProperty.c_str(), buffer, &nChars);
87 result = buffer;
90 return result;
93 // The provided GUID must be without surounding '{}'
94 static std::_tstring GetGuidPart(const std::_tstring& guid, int index)
96 assert((guid.length() == 36) && "No GUID or wrong format!");
97 assert(((index > -1) && (index < 5)) && "Out of range!");
99 if (index == 0) return std::_tstring(guid.c_str(), 8);
100 if (index == 1) return std::_tstring(guid.c_str() + 9, 4);
101 if (index == 2) return std::_tstring(guid.c_str() + 14, 4);
102 if (index == 3) return std::_tstring(guid.c_str() + 19, 4);
103 if (index == 4) return std::_tstring(guid.c_str() + 24, 12);
105 return std::_tstring();
108 static void Swap(char* p1, char* p2)
110 char tmp = *p1;
111 *p1 = *p2;
112 *p2 = tmp;
115 static std::_tstring Invert(const std::_tstring& str)
117 char* buff = reinterpret_cast<char*>(_alloca(str.length()));
118 strncpy(buff, str.c_str(), str.length());
120 char* front = buff;
121 char* back = buff + str.length() - 1;
123 while (front < back)
124 Swap(front++, back--);
126 return std::_tstring(buff, str.length());
129 // Convert the upgrade code (which is a GUID) according
130 // to the way the windows installer does when writing it
131 // to the registry
132 // The first 8 bytes will be inverted, from the the last
133 // 8 bytes always the nibbles will be inverted for further
134 // details look in the MSDN under compressed registry keys
135 static std::_tstring ConvertGuid(const std::_tstring& guid)
137 std::_tstring convertedGuid;
139 std::_tstring part = GetGuidPart(guid, 0);
140 convertedGuid = Invert(part);
142 part = GetGuidPart(guid, 1);
143 convertedGuid += Invert(part);
145 part = GetGuidPart(guid, 2);
146 convertedGuid += Invert(part);
148 part = GetGuidPart(guid, 3);
149 convertedGuid += Invert(std::_tstring(part.c_str(), 2));
150 convertedGuid += Invert(std::_tstring(part.c_str() + 2, 2));
152 part = GetGuidPart(guid, 4);
153 int pos = 0;
154 for (int i = 0; i < 6; i++)
156 convertedGuid += Invert(std::_tstring(part.c_str() + pos, 2));
157 pos += 2;
159 return convertedGuid;
162 static inline bool IsSetMsiProperty(MSIHANDLE handle, const std::_tstring& sProperty)
164 std::_tstring value = GetMsiProperty(handle, sProperty);
165 return (value.length() > 0);
168 static inline void UnsetMsiProperty(MSIHANDLE handle, const std::_tstring& sProperty)
170 MsiSetProperty(handle, sProperty.c_str(), NULL);
173 static inline void SetMsiProperty(MSIHANDLE handle, const std::_tstring& sProperty)
175 MsiSetProperty(handle, sProperty.c_str(), TEXT("1"));
178 static bool SwapFiles( const std::_tstring& sFileName1, const std::_tstring& sFileName2 )
180 std::_tstring sTempFileName = sFileName1 + TEXT(".tmp");
182 bool fSuccess = true;
184 //Try to move the original file to a temp file
185 fSuccess = MoveFileExA( sFileName1.c_str(), sTempFileName.c_str(), MOVEFILE_REPLACE_EXISTING);
187 if ( fSuccess )
189 fSuccess = MoveFileExA( sFileName2.c_str(), sFileName1.c_str(), MOVEFILE_REPLACE_EXISTING );
191 if ( fSuccess )
193 fSuccess = MoveFileExA( sTempFileName.c_str(), sFileName2.c_str(),
194 MOVEFILE_REPLACE_EXISTING );
195 if ( !fSuccess )
197 MoveFileExA( sFileName1.c_str(), sFileName2.c_str(), MOVEFILE_REPLACE_EXISTING );
200 else
202 MoveFileExA( sTempFileName.c_str(), sFileName1.c_str(), MOVEFILE_REPLACE_EXISTING );
205 else
207 //It could be that there is no original file and therefore copying the original to a temp
208 // file failed. Examine if there is no original and if so then move file2 to file1
210 WIN32_FIND_DATA data;
211 HANDLE hdl = FindFirstFile(sFileName1.c_str(), &data);
212 if (hdl == INVALID_HANDLE_VALUE)
214 fSuccess = MoveFileExA( sFileName2.c_str(), sFileName1.c_str(), MOVEFILE_REPLACE_EXISTING );
216 else
218 FindClose(hdl);
222 OutputDebugStringFormat( TEXT("%s <-> %s: %s"), sFileName1.c_str(), sFileName2.c_str(), fSuccess ? TEXT("OK") : TEXT("FAILED") );
224 if (!fSuccess )
226 DWORD dwError = GetLastError();
227 LPVOID lpMsgBuf;
228 if ( FormatMessage(
229 FORMAT_MESSAGE_ALLOCATE_BUFFER |
230 FORMAT_MESSAGE_FROM_SYSTEM |
231 FORMAT_MESSAGE_IGNORE_INSERTS,
232 NULL,
233 GetLastError(),
234 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
235 (LPTSTR) &lpMsgBuf,
237 NULL ))
239 OutputDebugStringFormat( TEXT("Error Code %d: %s"), dwError, lpMsgBuf );
240 LocalFree( lpMsgBuf );
242 else
243 OutputDebugStringFormat( TEXT("Error Code %d: Unknown"), dwError );
244 SetMsiErrorCode( dwError );
247 return fSuccess;
250 static std::_tstring strip( const std::_tstring& s, _TCHAR c )
252 std::_tstring result = s;
254 std::_tstring::size_type f;
258 f = result.find( c );
259 if ( f != std::_tstring::npos )
260 result.erase( f, 1 );
261 } while ( f != std::_tstring::npos );
263 return result;
266 static std::_tstring trim( const std::_tstring& rString )
268 std::_tstring temp = rString;
270 while ( temp.length() && temp[0] == ' ' || temp[0] == '\t' )
271 temp.erase( 0, 1 );
273 std::_tstring::size_type len = temp.length();
275 while ( len && temp[len-1] == ' ' || temp[len-1] == '\t' )
277 temp.erase( len - 1, 1 );
278 len = temp.length();
281 return temp;
284 static bool readLine( FILE *fp, std::_tstring& rLine )
286 _TCHAR szBuffer[1024];
287 bool bSuccess = false;
288 bool bEOL = false;
289 std::_tstring line;
292 while ( !bEOL && _fgetts( szBuffer, sizeof(szBuffer), fp ) )
294 int len = _tcslen(szBuffer);
296 bSuccess = true;
298 while ( len && szBuffer[len - 1] == '\n' )
300 szBuffer[--len] = 0;
301 bEOL = true;
304 line.append( szBuffer );
307 rLine = line;
308 return bSuccess;
312 static std::_tstring getProfileString(
313 const std::_tstring& aFileName,
314 const std::_tstring& aSectionName,
315 const std::_tstring& aKeyName,
316 const std::_tstring& aDefault = _T("") )
318 FILE *fp = _tfopen( aFileName.c_str(), _T("r") );
319 std::_tstring retValue = aDefault.length() ? aDefault : _T("");
321 if ( fp )
323 std::_tstring line;
324 std::_tstring section;
326 while ( readLine( fp, line ) )
328 line = trim( line );
330 if ( line.length() && line[0] == '[' )
332 line.erase( 0, 1 );
333 std::_tstring::size_type end = line.find( ']', 0 );
335 if ( std::_tstring::npos != end )
336 section = trim( line.substr( 0, end ) );
338 else
341 std::_tstring::size_type iEqualSign = line.find( '=', 0 );
343 if ( iEqualSign != std::_tstring::npos )
345 std::_tstring keyname = line.substr( 0, iEqualSign );
346 keyname = trim( keyname );
348 std::_tstring value = line.substr( iEqualSign + 1 /*, std::_tstring::npos */ );
349 value = trim( value );
351 if (
352 0 == _tcsicmp( section.c_str(), aSectionName.c_str() ) &&
353 0 == _tcsicmp( keyname.c_str(), aKeyName.c_str() )
356 retValue = value;
357 break;
363 fclose( fp );
366 return retValue;
369 static std::queue< std::_tstring > getProfileSections( const std::_tstring& aFileName )
371 FILE *fp = _tfopen( aFileName.c_str(), _T("r") );
372 std::queue< std::_tstring > aResult;
374 OutputDebugStringFormat( TEXT("*** Retrieving Section Names ****") );
376 if ( fp )
378 std::_tstring line;
379 std::_tstring section;
381 while ( readLine( fp, line ) )
383 line = trim( line );
385 if ( line.length() && line[0] == '[' )
387 line.erase( 0, 1 );
388 std::_tstring::size_type end = line.find( ']', 0 );
390 if ( std::_tstring::npos != end )
391 section = trim( line.substr( 0, end ) );
393 aResult.push( section );
395 OutputDebugStringFormat( TEXT("Section: %s"), section.c_str() );
400 fclose( fp );
403 OutputDebugStringFormat( TEXT("*** Done Section Names ***") );
405 return aResult;
408 static std::queue< std::_tstring > getProfileKeys( const std::_tstring& aFileName, const std::_tstring& aSectionName )
410 FILE *fp = _tfopen( aFileName.c_str(), _T("r") );
411 std::queue< std::_tstring > aResult;
413 OutputDebugStringFormat( TEXT("*** Retrieving Key Names for [%s] ***"), aSectionName.c_str() );
415 if ( fp )
417 std::_tstring line;
418 std::_tstring section;
420 while ( readLine( fp, line ) )
422 line = trim( line );
424 if ( line.length() && line[0] == '[' )
426 line.erase( 0, 1 );
427 std::_tstring::size_type end = line.find( ']', 0 );
429 if ( std::_tstring::npos != end )
430 section = trim( line.substr( 0, end ) );
432 else
435 std::_tstring::size_type iEqualSign = line.find( '=', 0 );
437 if ( iEqualSign != std::_tstring::npos )
439 std::_tstring keyname = line.substr( 0, iEqualSign );
440 keyname = trim( keyname );
442 if ( 0 == _tcsicmp( section.c_str(), aSectionName.c_str() ) )
444 aResult.push( keyname );
446 OutputDebugStringFormat( keyname.c_str() );
453 fclose( fp );
456 OutputDebugStringFormat( TEXT("*** Done Key Names for [%s] ***"), aSectionName.c_str() );
458 return aResult;
461 extern "C" UINT __stdcall InstallPatchedFiles( MSIHANDLE handle )
463 std::_tstring sInstDir = GetMsiProperty( handle, TEXT("INSTALLLOCATION") );
464 std::_tstring sProgramDir = sInstDir + TEXT("program\\");
465 std::_tstring sPatchFile = sProgramDir + TEXT("patchlist.txt");
467 std::queue< std::_tstring > aSectionNames;
468 std::queue< std::_tstring > aKeyNames;
470 OutputDebugStringA( "Starting Custom Action" );
472 aSectionNames = getProfileSections( sPatchFile );
473 while ( !aSectionNames.empty() )
475 std::_tstring sSectionName = aSectionNames.front();
476 if ( std::_tstring(TEXT("_root")) == sSectionName ) { sSectionName = TEXT(""); }
478 aKeyNames = getProfileKeys( sPatchFile, sSectionName );
479 while ( !aKeyNames.empty() )
481 std::_tstring sKeyName = aKeyNames.front();
482 std::_tstring sValue = getProfileString( sPatchFile, sSectionName, sKeyName );
484 if ( sValue.length() )
486 std::_tstring sFileName1 = sKeyName;
487 std::_tstring sExtension = sValue;
488 std::_tstring sFileName2;
490 sFileName1 = strip( sFileName1, '\"' );
491 sExtension = strip( sExtension, '\"' );
493 sFileName1 = sInstDir + sSectionName + sFileName1;
494 sFileName2 = sFileName1 + sExtension;
496 SwapFiles( sFileName1, sFileName2 );
499 aKeyNames.pop();
502 aSectionNames.pop();
505 return ERROR_SUCCESS;
508 extern "C" UINT __stdcall UninstallPatchedFiles( MSIHANDLE handle )
510 TCHAR szValue[8192];
511 DWORD nValueSize = sizeof(szValue);
512 HKEY hKey;
514 std::_tstring sInstDir;
516 std::_tstring sProductKey = GetMsiProperty( handle, TEXT("FINDPRODUCT") );
518 if ( ERROR_SUCCESS == RegOpenKey( HKEY_CURRENT_USER, sProductKey.c_str(), &hKey ) )
520 if ( ERROR_SUCCESS == RegQueryValueEx( hKey, TEXT("INSTALLLOCATION"), NULL, NULL, (LPBYTE)szValue, &nValueSize ) )
522 sInstDir = szValue;
524 RegCloseKey( hKey );
526 else if ( ERROR_SUCCESS == RegOpenKey( HKEY_LOCAL_MACHINE, sProductKey.c_str(), &hKey ) )
528 if ( ERROR_SUCCESS == RegQueryValueEx( hKey, TEXT("INSTALLLOCATION"), NULL, NULL, (LPBYTE)szValue, &nValueSize ) )
530 sInstDir = szValue;
532 RegCloseKey( hKey );
534 else
535 return ERROR_SUCCESS;
537 std::_tstring sProgramDir = sInstDir + TEXT("program\\");
538 std::_tstring sPatchFile = sProgramDir + TEXT("patchlist.txt");
540 std::queue< std::_tstring > aSectionNames;
541 std::queue< std::_tstring > aKeyNames;
543 aSectionNames = getProfileSections( sPatchFile );
544 while ( !aSectionNames.empty() )
546 std::_tstring sSectionName = aSectionNames.front();
547 if ( std::_tstring(TEXT("_root")) == sSectionName ) { sSectionName = TEXT(""); }
549 aKeyNames = getProfileKeys( sPatchFile, sSectionName );
550 while( !aKeyNames.empty() )
552 std::_tstring sKeyName = aKeyNames.front();
553 std::_tstring sValue = getProfileString( sPatchFile, sSectionName, sKeyName );
555 if ( sValue.length() )
557 std::_tstring sFileName1 = sKeyName;
558 std::_tstring sExtension = sValue;
559 std::_tstring sFileName2;
561 sFileName1 = strip( sFileName1, '\"' );
562 sExtension = strip( sExtension, '\"' );
564 sFileName1 = sInstDir + sSectionName + sFileName1;
565 sFileName2 = sFileName1 + sExtension;
567 SwapFiles( sFileName2, sFileName1 );
570 aKeyNames.pop();
573 aSectionNames.pop();
576 return ERROR_SUCCESS;
579 extern "C" UINT __stdcall SetFeatureState( MSIHANDLE handle )
582 // 1. Reading Product Code from setup.ini of installed Office
584 std::_tstring sInstallPath = GetMsiProperty(handle, TEXT("INSTALLLOCATION"));
585 std::_tstring sSetupiniPath = sInstallPath + TEXT("program\\setup.ini");
587 TCHAR szProductCode[32767];
589 GetPrivateProfileString(
590 TEXT("Bootstrap"),
591 TEXT("ProductCode"),
592 TEXT("NOTFOUND"),
593 szProductCode,
594 SAL_N_ELEMENTS(szProductCode),
595 sSetupiniPath.c_str()
598 if ( !_tcsicmp( szProductCode, TEXT("NOTFOUND") ) )
600 // No setup.ini or no "ProductCode" in setup.ini. This is an invalid directory.
601 return ERROR_SUCCESS;
604 // 2. Converting Product code
606 std::_tstring productCode = TEXT(szProductCode);
607 productCode = ConvertGuid(std::_tstring(productCode.c_str() + 1, productCode.length() - 2));
609 // 3. Setting path in the Windows registry to find installed features
611 std::_tstring registryKey;
612 HKEY registryRoot;
614 if ( IsSetMsiProperty(handle, TEXT("ALLUSERS")) )
616 registryRoot = HKEY_LOCAL_MACHINE;
617 registryKey = TEXT("Software\\Classes\\Installer\\Features\\") + productCode;
619 else
621 registryRoot = HKEY_CURRENT_USER;
622 registryKey = TEXT("Software\\Microsoft\\Installer\\Features\\") + productCode;
625 // 4. Collecting all installed features from Windows registry
627 HKEY hKey;
628 if (RegOpenKey(registryRoot, registryKey.c_str(), &hKey) == ERROR_SUCCESS)
630 int counter = 0;
631 LONG lEnumResult;
635 TCHAR szValueName[8192];
636 DWORD nValueNameSize = sizeof(szValueName);
637 LPDWORD pValueNameSize = &nValueNameSize;
638 TCHAR szValueData[8192];
639 DWORD nValueDataSize = sizeof(szValueData);
641 lEnumResult = RegEnumValue( hKey, counter, szValueName, pValueNameSize, NULL, NULL, (LPBYTE)szValueData, &nValueDataSize);
643 if ( ERROR_SUCCESS == lEnumResult )
645 std::_tstring sValueName = szValueName;
647 // Does this feature exist in this patch?
648 if ( IsSetMsiProperty(handle, sValueName) )
650 // Feature is not installed, if szValueData starts with a "square" (ascii 6)
651 if ( 6 == szValueData[0] )
653 MsiSetFeatureState(handle,sValueName.c_str(),INSTALLSTATE_ABSENT); // do not install this feature
655 else
657 MsiSetFeatureState(handle,sValueName.c_str(),INSTALLSTATE_LOCAL); // do install this feature
662 counter = counter + 1;
664 } while ( ERROR_SUCCESS == lEnumResult );
666 RegCloseKey( hKey );
669 return ERROR_SUCCESS;
672 extern "C" UINT __stdcall SetNewFeatureState( MSIHANDLE handle )
674 std::_tstring sValueName;
676 sValueName = TEXT("gm_o_Onlineupdate");
678 if (IsSetMsiProperty(handle, TEXT("SELECT_OU_FEATURE")))
680 MsiSetFeatureState(handle,sValueName.c_str(),INSTALLSTATE_LOCAL); // do install this feature
682 else
684 MsiSetFeatureState(handle,sValueName.c_str(),INSTALLSTATE_ABSENT); // do not install this feature
687 return ERROR_SUCCESS;
690 extern "C" UINT __stdcall ShowOnlineUpdateDialog( MSIHANDLE handle )
692 // Checking existence of file "updchk.uno.dll", which shows, that
693 // Online Update functionality is always available. Then the dialog
694 // that offers the Online Update is superfluous.
696 std::_tstring sInstDir = GetMsiProperty( handle, TEXT("INSTALLLOCATION") );
697 std::_tstring sProgramDir = sInstDir + TEXT("program\\");
698 std::_tstring sSearchFile = sProgramDir + TEXT("updchk.uno.dll");
700 WIN32_FIND_DATA data;
701 HANDLE hdl = FindFirstFile(sSearchFile.c_str(), &data);
702 if (hdl != INVALID_HANDLE_VALUE) // the file exists
704 // And finally setting property SHOW_ONLINEUPDATE_DIALOG
705 // to hide this dialog
706 UnsetMsiProperty(handle, TEXT("SHOW_ONLINEUPDATE_DIALOG"));
708 // Setting SELECT_OU_FEATURE to 1, which is probably superfluous
709 // because this is already the default value. But only this
710 // guarantees, that CustomAction SetNewFeatureState always sets
711 // the correct FeatureState for "gm_o_Onlineupdate", if it is
712 // already installed.
713 SetMsiProperty(handle, TEXT("SELECT_OU_FEATURE"));
715 else
717 // If the file does not exist, the Online Update dialog
718 // has to be shown.
719 SetMsiProperty(handle, TEXT("SHOW_ONLINEUPDATE_DIALOG"));
720 FindClose(hdl);
723 return ERROR_SUCCESS;
726 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */