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