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
32 #pragma warning(push, 1) /* disable warnings within system headers */
34 #define WIN32_LEAN_AND_MEAN
46 #define _tstring wstring
48 #define _tstring string
54 #include <sal/macros.h>
56 #include <systools/win32/uwinapi.h>
57 #include <../tools/seterror.hxx>
60 inline void OutputDebugStringFormat( LPCTSTR pFormat
, ... )
65 va_start( args
, pFormat
);
66 _vsntprintf( buffer
, SAL_N_ELEMENTS(buffer
), pFormat
, args
);
67 OutputDebugString( buffer
);
70 static inline void OutputDebugStringFormat( LPCTSTR
, ... )
75 static std::_tstring
GetMsiProperty( MSIHANDLE handle
, const std::_tstring
& sProperty
)
78 TCHAR szDummy
[1] = TEXT("");
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
);
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
)
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());
121 char* back
= buff
+ str
.length() - 1;
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
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);
154 for (int i
= 0; i
< 6; i
++)
156 convertedGuid
+= Invert(std::_tstring(part
.c_str() + 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
);
189 fSuccess
= MoveFileExA( sFileName2
.c_str(), sFileName1
.c_str(), MOVEFILE_REPLACE_EXISTING
);
193 fSuccess
= MoveFileExA( sTempFileName
.c_str(), sFileName2
.c_str(),
194 MOVEFILE_REPLACE_EXISTING
);
197 MoveFileExA( sFileName1
.c_str(), sFileName2
.c_str(), MOVEFILE_REPLACE_EXISTING
);
202 MoveFileExA( sTempFileName
.c_str(), sFileName1
.c_str(), MOVEFILE_REPLACE_EXISTING
);
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
);
222 OutputDebugStringFormat( TEXT("%s <-> %s: %s"), sFileName1
.c_str(), sFileName2
.c_str(), fSuccess
? TEXT("OK") : TEXT("FAILED") );
226 DWORD dwError
= GetLastError();
229 FORMAT_MESSAGE_ALLOCATE_BUFFER
|
230 FORMAT_MESSAGE_FROM_SYSTEM
|
231 FORMAT_MESSAGE_IGNORE_INSERTS
,
234 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
239 OutputDebugStringFormat( TEXT("Error Code %d: %s"), dwError
, lpMsgBuf
);
240 LocalFree( lpMsgBuf
);
243 OutputDebugStringFormat( TEXT("Error Code %d: Unknown"), dwError
);
244 SetMsiErrorCode( dwError
);
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
);
266 static std::_tstring
trim( const std::_tstring
& rString
)
268 std::_tstring temp
= rString
;
270 while ( temp
.length() && temp
[0] == ' ' || temp
[0] == '\t' )
273 std::_tstring::size_type len
= temp
.length();
275 while ( len
&& temp
[len
-1] == ' ' || temp
[len
-1] == '\t' )
277 temp
.erase( len
- 1, 1 );
284 static bool readLine( FILE *fp
, std::_tstring
& rLine
)
286 _TCHAR szBuffer
[1024];
287 bool bSuccess
= false;
292 while ( !bEOL
&& _fgetts( szBuffer
, sizeof(szBuffer
), fp
) )
294 int len
= _tcslen(szBuffer
);
298 while ( len
&& szBuffer
[len
- 1] == '\n' )
304 line
.append( szBuffer
);
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("");
324 std::_tstring section
;
326 while ( readLine( fp
, line
) )
330 if ( line
.length() && line
[0] == '[' )
333 std::_tstring::size_type end
= line
.find( ']', 0 );
335 if ( std::_tstring::npos
!= end
)
336 section
= trim( line
.substr( 0, end
) );
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
);
352 0 == _tcsicmp( section
.c_str(), aSectionName
.c_str() ) &&
353 0 == _tcsicmp( keyname
.c_str(), aKeyName
.c_str() )
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 ****") );
379 std::_tstring section
;
381 while ( readLine( fp
, line
) )
385 if ( line
.length() && line
[0] == '[' )
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() );
403 OutputDebugStringFormat( TEXT("*** Done Section Names ***") );
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() );
418 std::_tstring section
;
420 while ( readLine( fp
, line
) )
424 if ( line
.length() && line
[0] == '[' )
427 std::_tstring::size_type end
= line
.find( ']', 0 );
429 if ( std::_tstring::npos
!= end
)
430 section
= trim( line
.substr( 0, end
) );
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() );
456 OutputDebugStringFormat( TEXT("*** Done Key Names for [%s] ***"), aSectionName
.c_str() );
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
);
505 return ERROR_SUCCESS
;
508 extern "C" UINT __stdcall
UninstallPatchedFiles( MSIHANDLE handle
)
511 DWORD nValueSize
= sizeof(szValue
);
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
) )
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
) )
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
);
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(
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
;
614 if ( IsSetMsiProperty(handle
, TEXT("ALLUSERS")) )
616 registryRoot
= HKEY_LOCAL_MACHINE
;
617 registryKey
= TEXT("Software\\Classes\\Installer\\Features\\") + productCode
;
621 registryRoot
= HKEY_CURRENT_USER
;
622 registryKey
= TEXT("Software\\Microsoft\\Installer\\Features\\") + productCode
;
625 // 4. Collecting all installed features from Windows registry
628 if (RegOpenKey(registryRoot
, registryKey
.c_str(), &hKey
) == ERROR_SUCCESS
)
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
657 MsiSetFeatureState(handle
,sValueName
.c_str(),INSTALLSTATE_LOCAL
); // do install this feature
662 counter
= counter
+ 1;
664 } while ( ERROR_SUCCESS
== lEnumResult
);
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
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"));
717 // If the file does not exist, the Online Update dialog
719 SetMsiProperty(handle
, TEXT("SHOW_ONLINEUPDATE_DIALOG"));
723 return ERROR_SUCCESS
;
726 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */