* Sync up to trunk head (r64921).
[reactos.git] / 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 #include "msipriv.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(msi);
32
33 static BOOL check_language(DWORD lang1, LPCWSTR lang2, DWORD attributes)
34 {
35 DWORD langdword;
36
37 if (!lang2 || lang2[0]==0)
38 return TRUE;
39
40 langdword = atoiW(lang2);
41
42 if (attributes & msidbUpgradeAttributesLanguagesExclusive)
43 return (lang1 != langdword);
44 else
45 return (lang1 == langdword);
46 }
47
48 static void append_productcode(MSIPACKAGE* package, LPCWSTR action_property,
49 LPCWSTR productid)
50 {
51 LPWSTR prop;
52 LPWSTR newprop;
53 DWORD len;
54 UINT r;
55
56 prop = msi_dup_property(package->db, action_property );
57 if (prop)
58 len = strlenW(prop);
59 else
60 len = 0;
61
62 /*separator*/
63 len ++;
64
65 len += strlenW(productid);
66
67 /*null*/
68 len++;
69
70 newprop = msi_alloc( len*sizeof(WCHAR) );
71
72 if (prop)
73 {
74 strcpyW(newprop,prop);
75 strcatW(newprop,szSemiColon);
76 }
77 else
78 newprop[0] = 0;
79 strcatW(newprop,productid);
80
81 r = msi_set_property( package->db, action_property, newprop, -1 );
82 if (r == ERROR_SUCCESS && !strcmpW( action_property, szSourceDir ))
83 msi_reset_folders( package, TRUE );
84
85 TRACE("Found Related Product... %s now %s\n",
86 debugstr_w(action_property), debugstr_w(newprop));
87
88 msi_free( prop );
89 msi_free( newprop );
90 }
91
92 static UINT ITERATE_FindRelatedProducts(MSIRECORD *rec, LPVOID param)
93 {
94 MSIPACKAGE *package = param;
95 WCHAR product[GUID_SIZE];
96 DWORD index = 0;
97 DWORD attributes = 0;
98 DWORD sz = GUID_SIZE;
99 LPCWSTR upgrade_code;
100 HKEY hkey = 0;
101 UINT rc = ERROR_SUCCESS;
102 MSIRECORD *uirow;
103
104 upgrade_code = MSI_RecordGetString(rec,1);
105
106 rc = MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey, FALSE);
107 if (rc != ERROR_SUCCESS)
108 return ERROR_SUCCESS;
109
110 uirow = MSI_CreateRecord(1);
111 attributes = MSI_RecordGetInteger(rec,5);
112
113 while (rc == ERROR_SUCCESS)
114 {
115 rc = RegEnumValueW(hkey, index, product, &sz, NULL, NULL, NULL, NULL);
116 if (rc == ERROR_SUCCESS)
117 {
118 WCHAR productid[GUID_SIZE];
119 LPCWSTR ver, language, action_property;
120 DWORD check = 0, comp_ver, sz = 0x100;
121 HKEY hukey;
122 INT r;
123
124 TRACE("Looking at index %u product %s\n", index, debugstr_w(product));
125
126 unsquash_guid(product, productid);
127 if (MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_USERMANAGED, &hukey, FALSE) &&
128 MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, &hukey, FALSE) &&
129 MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_MACHINE, &hukey, FALSE))
130 {
131 TRACE("product key not found\n");
132 rc = ERROR_SUCCESS;
133 index ++;
134 continue;
135 }
136
137 sz = sizeof(DWORD);
138 RegQueryValueExW(hukey, INSTALLPROPERTY_VERSIONW, NULL, NULL, (LPBYTE)&check, &sz);
139
140 /* check version minimum */
141 ver = MSI_RecordGetString(rec,2);
142 if (ver)
143 {
144 comp_ver = msi_version_str_to_dword(ver);
145 r = check - comp_ver;
146 if (r < 0 || (r == 0 && !(attributes & msidbUpgradeAttributesVersionMinInclusive)))
147 {
148 TRACE("version below minimum\n");
149 RegCloseKey(hukey);
150 index ++;
151 continue;
152 }
153 }
154
155 /* check version maximum */
156 ver = MSI_RecordGetString(rec,3);
157 if (ver)
158 {
159 comp_ver = msi_version_str_to_dword(ver);
160 r = check - comp_ver;
161 if (r > 0 || (r == 0 && !(attributes & msidbUpgradeAttributesVersionMaxInclusive)))
162 {
163 RegCloseKey(hukey);
164 index ++;
165 continue;
166 }
167 TRACE("version above maximum\n");
168 }
169
170 /* check language */
171 sz = sizeof(DWORD);
172 RegQueryValueExW(hukey, INSTALLPROPERTY_LANGUAGEW, NULL, NULL, (LPBYTE)&check, &sz);
173 RegCloseKey(hukey);
174 language = MSI_RecordGetString(rec,4);
175 if (!check_language(check, language, attributes))
176 {
177 index ++;
178 TRACE("language doesn't match\n");
179 continue;
180 }
181 TRACE("found related product\n");
182
183 action_property = MSI_RecordGetString(rec, 7);
184 append_productcode(package, action_property, productid);
185 MSI_RecordSetStringW(uirow, 1, productid);
186 msi_ui_actiondata(package, szFindRelatedProducts, uirow);
187 }
188 index ++;
189 }
190 RegCloseKey(hkey);
191 msiobj_release( &uirow->hdr);
192
193 return ERROR_SUCCESS;
194 }
195
196 UINT ACTION_FindRelatedProducts(MSIPACKAGE *package)
197 {
198 static const WCHAR query[] = {
199 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
200 '`','U','p','g','r','a','d','e','`',0};
201 MSIQUERY *view;
202 UINT rc;
203
204 if (msi_get_property_int(package->db, szInstalled, 0))
205 {
206 TRACE("Skipping FindRelatedProducts action: product already installed\n");
207 return ERROR_SUCCESS;
208 }
209 if (msi_action_is_unique(package, szFindRelatedProducts))
210 {
211 TRACE("Skipping FindRelatedProducts action: already done in UI sequence\n");
212 return ERROR_SUCCESS;
213 }
214 else
215 msi_register_unique_action(package, szFindRelatedProducts);
216
217 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
218 if (rc != ERROR_SUCCESS)
219 return ERROR_SUCCESS;
220
221 rc = MSI_IterateRecords(view, NULL, ITERATE_FindRelatedProducts, package);
222 msiobj_release(&view->hdr);
223 return rc;
224 }