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 #undef UNICODE
29 #undef _UNICODE
30 
31 #define _WIN32_WINDOWS 0x0410
32 
33 #ifdef _MSC_VER
34 #pragma warning(push, 1) /* disable warnings within system headers */
35 #endif
36 #define WIN32_LEAN_AND_MEAN
37 #include <windows.h>
38 #include <msiquery.h>
39 #ifdef _MSC_VER
40 #pragma warning(pop)
41 #endif
42 
43 #include <malloc.h>
44 #include <assert.h>
45 
46 #include <tchar.h>
47 #include <string>
48 
49 using namespace std;
50 
51 namespace
52 {
53     // The provided GUID must be without surounding '{}'
54     string GetGuidPart(const string& guid, int index)
55     {
56         assert((guid.length() == 36) && "No GUID or wrong format!");
57         assert(((index > -1) && (index < 5)) && "Out of range!");
58 
59         if (index == 0) return string(guid.c_str(), 8);
60         if (index == 1) return string(guid.c_str() + 9, 4);
61         if (index == 2) return string(guid.c_str() + 14, 4);
62         if (index == 3) return string(guid.c_str() + 19, 4);
63         if (index == 4) return string(guid.c_str() + 24, 12);
64 
65         return string();
66     }
67 
68     void Swap(char* p1, char* p2)
69     {
70         char tmp = *p1;
71         *p1 = *p2;
72         *p2 = tmp;
73     }
74 
75     string Invert(const string& str)
76     {
77         char* buff = reinterpret_cast<char*>(_alloca(str.length()));
78         strncpy(buff, str.c_str(), str.length());
79 
80         char* front = buff;
81         char* back = buff + str.length() - 1;
82 
83         while (front < back)
84             Swap(front++, back--);
85 
86         return string(buff, str.length());
87     }
88 
89     // Convert the upgrade code (which is a GUID) according
90     // to the way the windows installer does when writing it
91     // to the registry
92     // The first 8 bytes will be inverted, from the the last
93     // 8 bytes always the nibbles will be inverted for further
94     // details look in the MSDN under compressed registry keys
95     string ConvertGuid(const string& guid)
96     {
97         string convertedGuid;
98 
99         string part = GetGuidPart(guid, 0);
100         convertedGuid = Invert(part);
101 
102         part = GetGuidPart(guid, 1);
103         convertedGuid += Invert(part);
104 
105         part = GetGuidPart(guid, 2);
106         convertedGuid += Invert(part);
107 
108         part = GetGuidPart(guid, 3);
109         convertedGuid += Invert(string(part.c_str(), 2));
110         convertedGuid += Invert(string(part.c_str() + 2, 2));
111 
112         part = GetGuidPart(guid, 4);
113         int pos = 0;
114         for (int i = 0; i < 6; i++)
115         {
116             convertedGuid += Invert(string(part.c_str() + pos, 2));
117             pos += 2;
118         }
119         return convertedGuid;
120     }
121 
122     string GetMsiProperty(MSIHANDLE handle, const string& sProperty)
123     {
124 	    string	result;
125 	    TCHAR	szDummy[1] = TEXT("");
126 	    DWORD	nChars = 0;
127 
128 	    if (MsiGetProperty(handle, sProperty.c_str(), szDummy, &nChars) == ERROR_MORE_DATA)
129 	    {
130             DWORD nBytes = ++nChars * sizeof(TCHAR);
131             LPTSTR buffer = reinterpret_cast<LPTSTR>(_alloca(nBytes));
132             ZeroMemory( buffer, nBytes );
133             MsiGetProperty(handle, sProperty.c_str(), buffer, &nChars);
134             result = buffer;
135 	    }
136 	    return	result;
137     }
138 
139     inline bool IsSetMsiProperty(MSIHANDLE handle, const string& sProperty)
140     {
141         return (GetMsiProperty(handle, sProperty).length() > 0);
142     }
143 
144     inline void UnsetMsiProperty(MSIHANDLE handle, const string& sProperty)
145     {
146         MsiSetProperty(handle, sProperty.c_str(), NULL);
147     }
148 
149     inline void SetMsiProperty(MSIHANDLE handle, const string& sProperty)
150     {
151         MsiSetProperty(handle, sProperty.c_str(), TEXT("1"));
152     }
153 
154     bool RegistryKeyHasUpgradeSubKey(
155         HKEY hRootKey, const string& regKey, const string& upgradeKey)
156     {
157         HKEY hKey;
158         if (RegOpenKey(hRootKey, regKey.c_str(), &hKey) == ERROR_SUCCESS)
159         {
160             DWORD nSubKeys;
161             DWORD lLongestSubKey;
162 
163             if (RegQueryInfoKey(
164                 hKey, NULL, NULL, NULL, &nSubKeys, &lLongestSubKey, NULL, NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
165             {
166                 LPTSTR buffer = reinterpret_cast<LPTSTR>(_alloca(lLongestSubKey + 1));
167 
168                 for (DWORD i = 0; i < nSubKeys; i++)
169                 {
170                     LONG ret = RegEnumKey(hKey, i, buffer, lLongestSubKey + 1);
171                     if ((ret == ERROR_SUCCESS) && (buffer == upgradeKey))
172                         return true;
173                 }
174             }
175         }
176         return false;
177     }
178 } // namespace
179 
180 extern "C" UINT __stdcall SetProductInstallMode(MSIHANDLE handle)
181 {
182     string upgradeCode = GetMsiProperty(handle, TEXT("UpgradeCode"));
183     upgradeCode = ConvertGuid(string(upgradeCode.c_str() + 1, upgradeCode.length() - 2));
184 
185     //MessageBox(NULL, upgradeCode.c_str(), TEXT("Debug"), MB_OK);
186 
187     if (RegistryKeyHasUpgradeSubKey(
188         HKEY_CURRENT_USER,
189         TEXT("Software\\Microsoft\\Installer\\UpgradeCodes"),
190         upgradeCode) && IsSetMsiProperty(handle, TEXT("ALLUSERS")))
191     {
192         UnsetMsiProperty(handle, TEXT("ALLUSERS"));
193         //MessageBox(NULL, "ALLUSERS removed", "DEBUG", MB_OK);
194     }
195     else if (RegistryKeyHasUpgradeSubKey(
196              HKEY_LOCAL_MACHINE,
197              TEXT("Software\\Classes\\Installer\\UpgradeCodes"),
198              upgradeCode) && !IsSetMsiProperty(handle, TEXT("ALLUSERS")))
199     {
200         SetMsiProperty(handle, TEXT("ALLUSERS"));
201         //MessageBox(NULL, "ALLUSERS set", "DEBUG", MB_OK);
202     }
203     return ERROR_SUCCESS;
204 }
205