[MSI]
[reactos.git] / reactos / dll / win32 / msi / upgrade.c
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
3 *
4 * Copyright 2005 Aric Stewart for CodeWeavers
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 /*
22 * Actions focused on in this module
23 *
24 * FindRelatedProducts
25 * MigrateFeatureStates (TODO)
26 * RemoveExistingProducts (TODO)
27 */
28
29 #define WIN32_NO_STATUS
30 #define _INC_WINDOWS
31 #define COM_NO_WINDOWS_H
32
33 //#include <stdarg.h>
34
35 #include <windef.h>
36 //#include "winbase.h"
37 //#include "winerror.h"
38 #include <winreg.h>
39 #include <wine/debug.h>
40 //#include "msidefs.h"
41 #include "msipriv.h"
42 //#include "winuser.h"
43 #include <wine/unicode.h>
44
45 WINE_DEFAULT_DEBUG_CHANNEL(msi);
46
47 static BOOL check_language(DWORD lang1, LPCWSTR lang2, DWORD attributes)
48 {
49 DWORD langdword;
50
51 if (!lang2 || lang2[0]==0)
52 return TRUE;
53
54 langdword = atoiW(lang2);
55
56 if (attributes & msidbUpgradeAttributesLanguagesExclusive)
57 return (lang1 != langdword);
58 else
59 return (lang1 == langdword);
60 }
61
62 static void append_productcode(MSIPACKAGE* package, LPCWSTR action_property,
63 LPCWSTR productid)
64 {
65 LPWSTR prop;
66 LPWSTR newprop;
67 DWORD len;
68 UINT r;
69
70 prop = msi_dup_property(package->db, action_property );
71 if (prop)
72 len = strlenW(prop);
73 else
74 len = 0;
75
76 /*separator*/
77 len ++;
78
79 len += strlenW(productid);
80
81 /*null*/
82 len++;
83
84 newprop = msi_alloc( len*sizeof(WCHAR) );
85
86 if (prop)
87 {
88 strcpyW(newprop,prop);
89 strcatW(newprop,szSemiColon);
90 }
91 else
92 newprop[0] = 0;
93 strcatW(newprop,productid);
94
95 r = msi_set_property( package->db, action_property, newprop, -1 );
96 if (r == ERROR_SUCCESS && !strcmpW( action_property, szSourceDir ))
97 msi_reset_folders( package, TRUE );
98
99 TRACE("Found Related Product... %s now %s\n",
100 debugstr_w(action_property), debugstr_w(newprop));
101
102 msi_free( prop );
103 msi_free( newprop );
104 }
105
106 static UINT ITERATE_FindRelatedProducts(MSIRECORD *rec, LPVOID param)
107 {
108 MSIPACKAGE *package = param;
109 WCHAR product[GUID_SIZE];
110 DWORD index = 0;
111 DWORD attributes = 0;
112 DWORD sz = GUID_SIZE;
113 LPCWSTR upgrade_code;
114 HKEY hkey = 0;
115 UINT rc = ERROR_SUCCESS;
116 MSIRECORD *uirow;
117
118 upgrade_code = MSI_RecordGetString(rec,1);
119
120 rc = MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey, FALSE);
121 if (rc != ERROR_SUCCESS)
122 return ERROR_SUCCESS;
123
124 uirow = MSI_CreateRecord(1);
125 attributes = MSI_RecordGetInteger(rec,5);
126
127 while (rc == ERROR_SUCCESS)
128 {
129 rc = RegEnumValueW(hkey, index, product, &sz, NULL, NULL, NULL, NULL);
130 if (rc == ERROR_SUCCESS)
131 {
132 WCHAR productid[GUID_SIZE];
133 LPCWSTR ver, language, action_property;
134 DWORD check = 0, comp_ver, sz = 0x100;
135 HKEY hukey;
136 INT r;
137
138 TRACE("Looking at index %u product %s\n", index, debugstr_w(product));
139
140 unsquash_guid(product, productid);
141 if (MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_USERMANAGED, &hukey, FALSE) &&
142 MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, &hukey, FALSE) &&
143 MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_MACHINE, &hukey, FALSE))
144 {
145 TRACE("product key not found\n");
146 rc = ERROR_SUCCESS;
147 index ++;
148 continue;
149 }
150
151 sz = sizeof(DWORD);
152 RegQueryValueExW(hukey, INSTALLPROPERTY_VERSIONW, NULL, NULL, (LPBYTE)&check, &sz);
153
154 /* check version minimum */
155 ver = MSI_RecordGetString(rec,2);
156 if (ver)
157 {
158 comp_ver = msi_version_str_to_dword(ver);
159 r = check - comp_ver;
160 if (r < 0 || (r == 0 && !(attributes & msidbUpgradeAttributesVersionMinInclusive)))
161 {
162 TRACE("version below minimum\n");
163 RegCloseKey(hukey);
164 index ++;
165 continue;
166 }
167 }
168
169 /* check version maximum */
170 ver = MSI_RecordGetString(rec,3);
171 if (ver)
172 {
173 comp_ver = msi_version_str_to_dword(ver);
174 r = check - comp_ver;
175 if (r > 0 || (r == 0 && !(attributes & msidbUpgradeAttributesVersionMaxInclusive)))
176 {
177 RegCloseKey(hukey);
178 index ++;
179 continue;
180 }
181 TRACE("version above maximum\n");
182 }
183
184 /* check language */
185 sz = sizeof(DWORD);
186 RegQueryValueExW(hukey, INSTALLPROPERTY_LANGUAGEW, NULL, NULL, (LPBYTE)&check, &sz);
187 RegCloseKey(hukey);
188 language = MSI_RecordGetString(rec,4);
189 if (!check_language(check, language, attributes))
190 {
191 index ++;
192 TRACE("language doesn't match\n");
193 continue;
194 }
195 TRACE("found related product\n");
196
197 action_property = MSI_RecordGetString(rec, 7);
198 append_productcode(package, action_property, productid);
199 MSI_RecordSetStringW(uirow, 1, productid);
200 msi_ui_actiondata(package, szFindRelatedProducts, uirow);
201 }
202 index ++;
203 }
204 RegCloseKey(hkey);
205 msiobj_release( &uirow->hdr);
206
207 return ERROR_SUCCESS;
208 }
209
210 UINT ACTION_FindRelatedProducts(MSIPACKAGE *package)
211 {
212 static const WCHAR query[] = {
213 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
214 '`','U','p','g','r','a','d','e','`',0};
215 MSIQUERY *view;
216 UINT rc;
217
218 if (msi_get_property_int(package->db, szInstalled, 0))
219 {
220 TRACE("Skipping FindRelatedProducts action: product already installed\n");
221 return ERROR_SUCCESS;
222 }
223 if (msi_action_is_unique(package, szFindRelatedProducts))
224 {
225 TRACE("Skipping FindRelatedProducts action: already done in UI sequence\n");
226 return ERROR_SUCCESS;
227 }
228 else
229 msi_register_unique_action(package, szFindRelatedProducts);
230
231 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
232 if (rc != ERROR_SUCCESS)
233 return ERROR_SUCCESS;
234
235 rc = MSI_IterateRecords(view, NULL, ITERATE_FindRelatedProducts, package);
236 msiobj_release(&view->hdr);
237 return rc;
238 }