xref: /reactos/dll/win32/msi/registry.c (revision 84ccccab)
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2005 Mike McCormack for CodeWeavers
5  * Copyright 2005 Aric Stewart for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include "msipriv.h"
23 
24 WINE_DEFAULT_DEBUG_CHANNEL(msi);
25 
26 /*
27  * This module will be all the helper functions for registry access by the
28  * installer bits.
29  */
30 
31 static const WCHAR szUserDataFeatures_fmt[] = {
32     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
33     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
34     'I','n','s','t','a','l','l','e','r','\\','U','s','e','r','D','a','t','a','\\',
35     '%','s','\\','P','r','o','d','u','c','t','s','\\','%','s','\\','F','e','a','t','u','r','e','s',0};
36 
37 static const WCHAR szUserDataComp_fmt[] = {
38     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
39     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
40     'I','n','s','t','a','l','l','e','r','\\','U','s','e','r','D','a','t','a','\\',
41     '%','s','\\','C','o','m','p','o','n','e','n','t','s','\\','%','s',0};
42 
43 static const WCHAR szUserDataComponents_fmt[] = {
44     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
45     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
46     'I','n','s','t','a','l','l','e','r','\\','U','s','e','r','D','a','t','a','\\',
47     '%','s','\\','C','o','m','p','o','n','e','n','t','s',0};
48 
49 static const WCHAR szUserDataProd_fmt[] = {
50     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
51     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
52     'I','n','s','t','a','l','l','e','r','\\','U','s','e','r','D','a','t','a','\\',
53     '%','s','\\','P','r','o','d','u','c','t','s','\\','%','s',0};
54 
55 static const WCHAR szUserDataProducts_fmt[] = {
56     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
57     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
58     'I','n','s','t','a','l','l','e','r','\\','U','s','e','r','D','a','t','a','\\',
59     '%','s','\\','P','r','o','d','u','c','t','s',0};
60 
61 static const WCHAR szUserDataPatch_fmt[] = {
62     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
63     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
64     'I','n','s','t','a','l','l','e','r','\\','U','s','e','r','D','a','t','a','\\',
65     '%','s','\\','P','a','t','c','h','e','s','\\','%','s',0};
66 
67 static const WCHAR szUserDataPatches_fmt[] = {
68     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
69     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
70     'I','n','s','t','a','l','l','e','r','\\','U','s','e','r','D','a','t','a','\\',
71     '%','s','\\','P','a','t','c','h','e','s',0};
72 
73 static const WCHAR szUserDataProductPatches_fmt[] = {
74     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
75     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
76     'I','n','s','t','a','l','l','e','r','\\','U','s','e','r','D','a','t','a','\\',
77     '%','s','\\','P','r','o','d','u','c','t','s','\\','%','s','\\','P','a','t','c','h','e','s',0};
78 
79 static const WCHAR szInstallProperties_fmt[] = {
80     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
81     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
82     'I','n','s','t','a','l','l','e','r','\\','U','s','e','r','D','a','t','a','\\',
83     '%','s','\\','P','r','o','d','u','c','t','s','\\','%','s','\\',
84     'I','n','s','t','a','l','l','P','r','o','p','e','r','t','i','e','s',0};
85 
86 static const WCHAR szInstaller_LocalManagedProd_fmt[] = {
87     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
88     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
89     'I','n','s','t','a','l','l','e','r','\\','M','a','n','a','g','e','d','\\','%','s','\\',
90     'I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s','\\','%','s',0};
91 
92 static const WCHAR szInstaller_LocalManagedFeat_fmt[] = {
93     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
94     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
95     'I','n','s','t','a','l','l','e','r','\\','M','a','n','a','g','e','d','\\','%','s','\\',
96     'I','n','s','t','a','l','l','e','r','\\','F','e','a','t','u','r','e','s','\\','%','s',0};
97 
98 static const WCHAR szInstaller_Products[] = {
99     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
100     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
101     'I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s',0};
102 
103 static const WCHAR szInstaller_Patches[] = {
104     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
105     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
106     'I','n','s','t','a','l','l','e','r','\\','P','a','t','c','h','e','s',0};
107 
108 static const WCHAR szInstaller_LocalClassesProducts[] = {
109     'S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s','\\',
110     'I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s',0};
111 
112 static const WCHAR szInstaller_LocalClassesFeatures[] = {
113     'S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s','\\',
114     'I','n','s','t','a','l','l','e','r','\\','F','e','a','t','u','r','e','s',0};
115 
116 static const WCHAR szInstaller_LocalClassesProd[] = {
117     'S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s','\\',
118     'I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s','\\',0};
119 
120 static const WCHAR szInstaller_LocalClassesFeat[] = {
121     'S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s','\\',
122     'I','n','s','t','a','l','l','e','r','\\','F','e','a','t','u','r','e','s','\\',0};
123 
124 static const WCHAR szInstaller_ClassesUpgradeCode[] = {
125     'S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s','\\',
126     'I','n','s','t','a','l','l','e','r','\\','U','p','g','r','a','d','e','C','o','d','e','s','\\',0};
127 
128 static const WCHAR szInstaller_ClassesUpgradeCodes[] = {
129     'S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s','\\',
130     'I','n','s','t','a','l','l','e','r','\\','U','p','g','r','a','d','e','C','o','d','e','s',0};
131 
132 static const WCHAR szInstaller_Features[] = {
133     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
134     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
135     'I','n','s','t','a','l','l','e','r','\\','F','e','a','t','u','r','e','s','\\',0};
136 
137 static const WCHAR szInstaller_UpgradeCodes[] = {
138     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
139     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
140     'I','n','s','t','a','l','l','e','r','\\','U','p','g','r','a','d','e','C','o','d','e','s','\\',0};
141 
142 static const WCHAR szInstaller_UserUpgradeCodes[] = {
143     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
144     'I','n','s','t','a','l','l','e','r','\\','U','p','g','r','a','d','e','C','o','d','e','s','\\',0};
145 
146 static const WCHAR szUninstall[] = {
147     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
148     'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
149     'U','n','i','n','s','t','a','l','l','\\',0};
150 
151 static const WCHAR szUninstall_32node[] = {
152     'S','o','f','t','w','a','r','e','\\','W','o','w','6','4','3','2','N','o','d','e','\\',
153     'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
154     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','U','n','i','n','s','t','a','l','l','\\',0};
155 
156 static const WCHAR szUserComponents[] = {
157     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
158     'I','n','s','t','a','l','l','e','r','\\','C','o','m','p','o','n','e','n','t','s','\\',0};
159 
160 static const WCHAR szInstaller_Components[] = {
161     'S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s','\\',
162     'I','n','s','t','a','l','l','e','r','\\','C','o','m','p','o','n','e','n','t','s','\\',0};
163 
164 static const WCHAR szUserFeatures[] = {
165     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
166     'I','n','s','t','a','l','l','e','r','\\','F','e','a','t','u','r','e','s','\\',0};
167 
168 static const WCHAR szUserProducts[] = {
169     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
170     'I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s','\\',0};
171 
172 static const WCHAR szUserPatches[] = {
173     'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
174     'I','n','s','t','a','l','l','e','r','\\','P','a','t','c','h','e','s','\\',0};
175 
176 BOOL unsquash_guid(LPCWSTR in, LPWSTR out)
177 {
178     DWORD i,n=0;
179 
180     if (lstrlenW(in) != 32)
181         return FALSE;
182 
183     out[n++]='{';
184     for(i=0; i<8; i++)
185         out[n++] = in[7-i];
186     out[n++]='-';
187     for(i=0; i<4; i++)
188         out[n++] = in[11-i];
189     out[n++]='-';
190     for(i=0; i<4; i++)
191         out[n++] = in[15-i];
192     out[n++]='-';
193     for(i=0; i<2; i++)
194     {
195         out[n++] = in[17+i*2];
196         out[n++] = in[16+i*2];
197     }
198     out[n++]='-';
199     for( ; i<8; i++)
200     {
201         out[n++] = in[17+i*2];
202         out[n++] = in[16+i*2];
203     }
204     out[n++]='}';
205     out[n]=0;
206     return TRUE;
207 }
208 
209 BOOL squash_guid(LPCWSTR in, LPWSTR out)
210 {
211     DWORD i,n=1;
212     GUID guid;
213 
214     out[0] = 0;
215 
216     if (FAILED(CLSIDFromString((LPCOLESTR)in, &guid)))
217         return FALSE;
218 
219     for(i=0; i<8; i++)
220         out[7-i] = in[n++];
221     n++;
222     for(i=0; i<4; i++)
223         out[11-i] = in[n++];
224     n++;
225     for(i=0; i<4; i++)
226         out[15-i] = in[n++];
227     n++;
228     for(i=0; i<2; i++)
229     {
230         out[17+i*2] = in[n++];
231         out[16+i*2] = in[n++];
232     }
233     n++;
234     for( ; i<8; i++)
235     {
236         out[17+i*2] = in[n++];
237         out[16+i*2] = in[n++];
238     }
239     out[32]=0;
240     return TRUE;
241 }
242 
243 
244 /* tables for encoding and decoding base85 */
245 static const unsigned char table_dec85[0x80] = {
246 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
247 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
248 0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff,
249 0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17,
250 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
251 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36,
252 0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
253 0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff,
254 };
255 
256 static const char table_enc85[] =
257 "!$%&'()*+,-.0123456789=?@ABCDEFGHIJKLMNO"
258 "PQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwx"
259 "yz{}~";
260 
261 /*
262  *  Converts a base85 encoded guid into a GUID pointer
263  *  Base85 encoded GUIDs should be 20 characters long.
264  *
265  *  returns TRUE if successful, FALSE if not
266  */
267 BOOL decode_base85_guid( LPCWSTR str, GUID *guid )
268 {
269     DWORD i, val = 0, base = 1, *p;
270 
271     if (!str)
272         return FALSE;
273 
274     p = (DWORD*) guid;
275     for( i=0; i<20; i++ )
276     {
277         if( (i%5) == 0 )
278         {
279             val = 0;
280             base = 1;
281         }
282         val += table_dec85[str[i]] * base;
283         if( str[i] >= 0x80 )
284             return FALSE;
285         if( table_dec85[str[i]] == 0xff )
286             return FALSE;
287         if( (i%5) == 4 )
288             p[i/5] = val;
289         base *= 85;
290     }
291     return TRUE;
292 }
293 
294 /*
295  *  Encodes a base85 guid given a GUID pointer
296  *  Caller should provide a 21 character buffer for the encoded string.
297  *
298  *  returns TRUE if successful, FALSE if not
299  */
300 BOOL encode_base85_guid( GUID *guid, LPWSTR str )
301 {
302     unsigned int x, *p, i;
303 
304     p = (unsigned int*) guid;
305     for( i=0; i<4; i++ )
306     {
307         x = p[i];
308         *str++ = table_enc85[x%85];
309         x = x/85;
310         *str++ = table_enc85[x%85];
311         x = x/85;
312         *str++ = table_enc85[x%85];
313         x = x/85;
314         *str++ = table_enc85[x%85];
315         x = x/85;
316         *str++ = table_enc85[x%85];
317     }
318     *str = 0;
319 
320     return TRUE;
321 }
322 
323 DWORD msi_version_str_to_dword(LPCWSTR p)
324 {
325     DWORD major, minor = 0, build = 0, version = 0;
326 
327     if (!p)
328         return version;
329 
330     major = atoiW(p);
331 
332     p = strchrW(p, '.');
333     if (p)
334     {
335         minor = atoiW(p+1);
336         p = strchrW(p+1, '.');
337         if (p)
338             build = atoiW(p+1);
339     }
340 
341     return MAKELONG(build, MAKEWORD(minor, major));
342 }
343 
344 LONG msi_reg_set_val_str( HKEY hkey, LPCWSTR name, LPCWSTR value )
345 {
346     DWORD len;
347     if (!value) value = szEmpty;
348     len = (lstrlenW(value) + 1) * sizeof (WCHAR);
349     return RegSetValueExW( hkey, name, 0, REG_SZ, (const BYTE *)value, len );
350 }
351 
352 LONG msi_reg_set_val_multi_str( HKEY hkey, LPCWSTR name, LPCWSTR value )
353 {
354     LPCWSTR p = value;
355     while (*p) p += lstrlenW(p) + 1;
356     return RegSetValueExW( hkey, name, 0, REG_MULTI_SZ,
357                            (const BYTE *)value, (p + 1 - value) * sizeof(WCHAR) );
358 }
359 
360 LONG msi_reg_set_val_dword( HKEY hkey, LPCWSTR name, DWORD val )
361 {
362     return RegSetValueExW( hkey, name, 0, REG_DWORD, (LPBYTE)&val, sizeof (DWORD) );
363 }
364 
365 LONG msi_reg_set_subkey_val( HKEY hkey, LPCWSTR path, LPCWSTR name, LPCWSTR val )
366 {
367     HKEY hsubkey = 0;
368     LONG r;
369 
370     r = RegCreateKeyW( hkey, path, &hsubkey );
371     if (r != ERROR_SUCCESS)
372         return r;
373     r = msi_reg_set_val_str( hsubkey, name, val );
374     RegCloseKey( hsubkey );
375     return r;
376 }
377 
378 LPWSTR msi_reg_get_val_str( HKEY hkey, LPCWSTR name )
379 {
380     DWORD len = 0;
381     LPWSTR val;
382     LONG r;
383 
384     r = RegQueryValueExW(hkey, name, NULL, NULL, NULL, &len);
385     if (r != ERROR_SUCCESS)
386         return NULL;
387 
388     len += sizeof (WCHAR);
389     val = msi_alloc( len );
390     if (!val)
391         return NULL;
392     val[0] = 0;
393     RegQueryValueExW(hkey, name, NULL, NULL, (LPBYTE) val, &len);
394     return val;
395 }
396 
397 BOOL msi_reg_get_val_dword( HKEY hkey, LPCWSTR name, DWORD *val)
398 {
399     DWORD type, len = sizeof (DWORD);
400     LONG r = RegQueryValueExW(hkey, name, NULL, &type, (LPBYTE) val, &len);
401     return r == ERROR_SUCCESS && type == REG_DWORD;
402 }
403 
404 static WCHAR *get_user_sid(void)
405 {
406     HANDLE token;
407     DWORD size = 256;
408     TOKEN_USER *user;
409     WCHAR *ret;
410 
411     if (!OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &token )) return NULL;
412     if (!(user = msi_alloc( size )))
413     {
414         CloseHandle( token );
415         return NULL;
416     }
417     if (!GetTokenInformation( token, TokenUser, user, size, &size ))
418     {
419         msi_free( user );
420         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || !(user = msi_alloc( size )))
421         {
422             CloseHandle( token );
423             return NULL;
424         }
425         GetTokenInformation( token, TokenUser, user, size, &size );
426     }
427     CloseHandle( token );
428     if (!ConvertSidToStringSidW( user->User.Sid, &ret ))
429     {
430         msi_free( user );
431         return NULL;
432     }
433     msi_free( user );
434     return ret;
435 }
436 
437 UINT MSIREG_OpenUninstallKey(const WCHAR *product, enum platform platform, HKEY *key, BOOL create)
438 {
439     WCHAR keypath[0x200];
440 
441     TRACE("%s\n", debugstr_w(product));
442 
443     if (is_64bit && platform == PLATFORM_INTEL)
444     {
445         strcpyW(keypath, szUninstall_32node);
446         strcatW(keypath, product);
447     }
448     else
449     {
450         strcpyW(keypath, szUninstall);
451         strcatW(keypath, product);
452     }
453     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, KEY_ALL_ACCESS, NULL, key, NULL);
454     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, KEY_ALL_ACCESS, key);
455 }
456 
457 UINT MSIREG_DeleteUninstallKey(const WCHAR *product, enum platform platform)
458 {
459     WCHAR keypath[0x200];
460 
461     TRACE("%s\n", debugstr_w(product));
462 
463     if (is_64bit && platform == PLATFORM_INTEL)
464     {
465         strcpyW(keypath, szUninstall_32node);
466         strcatW(keypath, product);
467     }
468     else
469     {
470         strcpyW(keypath, szUninstall);
471         strcatW(keypath, product);
472     }
473     return RegDeleteTreeW(HKEY_LOCAL_MACHINE, keypath);
474 }
475 
476 UINT MSIREG_OpenProductKey(LPCWSTR szProduct, LPCWSTR szUserSid, MSIINSTALLCONTEXT context, HKEY *key, BOOL create)
477 {
478     HKEY root = HKEY_LOCAL_MACHINE;
479     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
480     WCHAR *usersid = NULL, squashed_pc[SQUASHED_GUID_SIZE], keypath[MAX_PATH];
481 
482     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
483     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
484 
485     if (context == MSIINSTALLCONTEXT_MACHINE)
486     {
487         strcpyW(keypath, szInstaller_LocalClassesProd);
488         strcatW( keypath, squashed_pc );
489     }
490     else if (context == MSIINSTALLCONTEXT_USERUNMANAGED)
491     {
492         root = HKEY_CURRENT_USER;
493         strcpyW(keypath, szUserProducts);
494         strcatW( keypath, squashed_pc );
495     }
496     else
497     {
498         if (!szUserSid)
499         {
500             if (!(usersid = get_user_sid()))
501             {
502                 ERR("Failed to retrieve user SID\n");
503                 return ERROR_FUNCTION_FAILED;
504             }
505             szUserSid = usersid;
506         }
507         sprintfW( keypath, szInstaller_LocalManagedProd_fmt, szUserSid, squashed_pc );
508         LocalFree(usersid);
509     }
510     if (create) return RegCreateKeyExW(root, keypath, 0, NULL, 0, access, NULL, key, NULL);
511     return RegOpenKeyExW(root, keypath, 0, access, key);
512 }
513 
514 UINT MSIREG_DeleteUserProductKey(LPCWSTR szProduct)
515 {
516     WCHAR squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200];
517 
518     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
519     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
520 
521     strcpyW(keypath, szUserProducts);
522     strcatW( keypath, squashed_pc );
523     return RegDeleteTreeW(HKEY_CURRENT_USER, keypath);
524 }
525 
526 UINT MSIREG_OpenUserPatchesKey(LPCWSTR szPatch, HKEY *key, BOOL create)
527 {
528     WCHAR squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200];
529 
530     if (!squash_guid( szPatch, squashed_pc )) return ERROR_FUNCTION_FAILED;
531     TRACE("%s squashed %s\n", debugstr_w(szPatch), debugstr_w(squashed_pc));
532 
533     strcpyW(keypath, szUserPatches);
534     strcatW( keypath, squashed_pc );
535 
536     if (create) return RegCreateKeyW(HKEY_CURRENT_USER, keypath, key);
537     return RegOpenKeyW(HKEY_CURRENT_USER, keypath, key);
538 }
539 
540 UINT MSIREG_OpenFeaturesKey(LPCWSTR szProduct, LPCWSTR szUserSid, MSIINSTALLCONTEXT context,
541                             HKEY *key, BOOL create)
542 {
543     HKEY root = HKEY_LOCAL_MACHINE;
544     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
545     WCHAR squashed_pc[SQUASHED_GUID_SIZE], keypath[MAX_PATH], *usersid = NULL;
546 
547     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
548     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
549 
550     if (context == MSIINSTALLCONTEXT_MACHINE)
551     {
552         strcpyW(keypath, szInstaller_LocalClassesFeat);
553         strcatW( keypath, squashed_pc );
554     }
555     else if (context == MSIINSTALLCONTEXT_USERUNMANAGED)
556     {
557         root = HKEY_CURRENT_USER;
558         strcpyW(keypath, szUserFeatures);
559         strcatW( keypath, squashed_pc );
560     }
561     else
562     {
563         if (!szUserSid)
564         {
565             if (!(usersid = get_user_sid()))
566             {
567                 ERR("Failed to retrieve user SID\n");
568                 return ERROR_FUNCTION_FAILED;
569             }
570             szUserSid = usersid;
571         }
572         sprintfW( keypath, szInstaller_LocalManagedFeat_fmt, szUserSid, squashed_pc );
573         LocalFree(usersid);
574     }
575     if (create) return RegCreateKeyExW(root, keypath, 0, NULL, 0, access, NULL, key, NULL);
576     return RegOpenKeyExW(root, keypath, 0, access, key);
577 }
578 
579 UINT MSIREG_DeleteUserFeaturesKey(LPCWSTR szProduct)
580 {
581     WCHAR squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200];
582 
583     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
584     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
585 
586     strcpyW(keypath, szUserFeatures);
587     strcatW( keypath, squashed_pc );
588     return RegDeleteTreeW(HKEY_CURRENT_USER, keypath);
589 }
590 
591 static UINT MSIREG_OpenInstallerFeaturesKey(LPCWSTR szProduct, HKEY *key, BOOL create)
592 {
593     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
594     WCHAR squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200];
595 
596     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
597     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
598 
599     strcpyW(keypath, szInstaller_Features);
600     strcatW( keypath, squashed_pc );
601 
602     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
603     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
604 }
605 
606 UINT MSIREG_OpenUserDataFeaturesKey(LPCWSTR szProduct, LPCWSTR szUserSid, MSIINSTALLCONTEXT context,
607                                     HKEY *key, BOOL create)
608 {
609     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
610     WCHAR squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200], *usersid = NULL;
611 
612     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
613     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
614 
615     if (context == MSIINSTALLCONTEXT_MACHINE)
616     {
617         sprintfW( keypath, szUserDataFeatures_fmt, szLocalSid, squashed_pc );
618     }
619     else
620     {
621         if (!szUserSid)
622         {
623             if (!(usersid = get_user_sid()))
624             {
625                 ERR("Failed to retrieve user SID\n");
626                 return ERROR_FUNCTION_FAILED;
627             }
628             szUserSid = usersid;
629         }
630         sprintfW( keypath, szUserDataFeatures_fmt, szUserSid, squashed_pc );
631         LocalFree(usersid);
632     }
633     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
634     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
635 }
636 
637 UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY *key, BOOL create)
638 {
639     WCHAR squashed_cc[SQUASHED_GUID_SIZE], keypath[0x200];
640     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
641     UINT ret;
642 
643     if (!squash_guid( szComponent, squashed_cc)) return ERROR_FUNCTION_FAILED;
644     TRACE("%s squashed %s\n", debugstr_w(szComponent), debugstr_w(squashed_cc));
645 
646     strcpyW(keypath, szUserComponents);
647     strcatW( keypath, squashed_cc );
648 
649     if (create) return RegCreateKeyW(HKEY_CURRENT_USER, keypath, key);
650     ret = RegOpenKeyW(HKEY_CURRENT_USER, keypath, key);
651     if (ret != ERROR_FILE_NOT_FOUND) return ret;
652 
653     strcpyW(keypath, szInstaller_Components);
654     strcatW( keypath, squashed_cc );
655     return RegOpenKeyExW( HKEY_LOCAL_MACHINE, keypath, 0, access, key );
656 }
657 
658 UINT MSIREG_OpenUserDataComponentKey(LPCWSTR szComponent, LPCWSTR szUserSid, HKEY *key, BOOL create)
659 {
660     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
661     WCHAR *usersid, squashed_comp[SQUASHED_GUID_SIZE], keypath[0x200];
662 
663     if (!squash_guid( szComponent, squashed_comp )) return ERROR_FUNCTION_FAILED;
664     TRACE("%s squashed %s\n", debugstr_w(szComponent), debugstr_w(squashed_comp));
665 
666     if (!szUserSid)
667     {
668         if (!(usersid = get_user_sid()))
669         {
670             ERR("Failed to retrieve user SID\n");
671             return ERROR_FUNCTION_FAILED;
672         }
673         sprintfW( keypath, szUserDataComp_fmt, usersid, squashed_comp );
674         LocalFree(usersid);
675     }
676     else
677         sprintfW( keypath, szUserDataComp_fmt, szUserSid, squashed_comp );
678 
679     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
680     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
681 }
682 
683 UINT MSIREG_DeleteUserDataComponentKey(LPCWSTR szComponent, LPCWSTR szUserSid)
684 {
685     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
686     WCHAR *usersid, squashed_comp[SQUASHED_GUID_SIZE], keypath[0x200];
687     HKEY hkey;
688     LONG r;
689 
690     if (!squash_guid( szComponent, squashed_comp )) return ERROR_FUNCTION_FAILED;
691     TRACE("%s squashed %s\n", debugstr_w(szComponent), debugstr_w(squashed_comp));
692 
693     if (!szUserSid)
694     {
695         if (!(usersid = get_user_sid()))
696         {
697             ERR("Failed to retrieve user SID\n");
698             return ERROR_FUNCTION_FAILED;
699         }
700         sprintfW(keypath, szUserDataComponents_fmt, usersid);
701         LocalFree(usersid);
702     }
703     else
704         sprintfW(keypath, szUserDataComponents_fmt, szUserSid);
705 
706     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, &hkey)) return ERROR_SUCCESS;
707     r = RegDeleteTreeW( hkey, squashed_comp );
708     RegCloseKey(hkey);
709     return r;
710 }
711 
712 UINT MSIREG_OpenUserDataProductKey(LPCWSTR szProduct, MSIINSTALLCONTEXT dwContext, LPCWSTR szUserSid, HKEY *key, BOOL create)
713 {
714     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
715     WCHAR *usersid, squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200];
716 
717     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
718     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
719 
720     if (dwContext == MSIINSTALLCONTEXT_MACHINE)
721         sprintfW( keypath, szUserDataProd_fmt, szLocalSid, squashed_pc );
722     else if (szUserSid)
723         sprintfW( keypath, szUserDataProd_fmt, szUserSid, squashed_pc );
724     else
725     {
726         if (!(usersid = get_user_sid()))
727         {
728             ERR("Failed to retrieve user SID\n");
729             return ERROR_FUNCTION_FAILED;
730         }
731         sprintfW( keypath, szUserDataProd_fmt, usersid, squashed_pc );
732         LocalFree(usersid);
733     }
734     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
735     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
736 }
737 
738 UINT MSIREG_OpenUserDataPatchKey(LPCWSTR szPatch, MSIINSTALLCONTEXT dwContext, HKEY *key, BOOL create)
739 {
740     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
741     WCHAR *usersid, squashed_patch[SQUASHED_GUID_SIZE], keypath[0x200];
742 
743     if (!squash_guid( szPatch, squashed_patch )) return ERROR_FUNCTION_FAILED;
744     TRACE("%s squashed %s\n", debugstr_w(szPatch), debugstr_w(squashed_patch));
745 
746     if (dwContext == MSIINSTALLCONTEXT_MACHINE)
747         sprintfW( keypath, szUserDataPatch_fmt, szLocalSid, squashed_patch );
748     else
749     {
750         if (!(usersid = get_user_sid()))
751         {
752             ERR("Failed to retrieve user SID\n");
753             return ERROR_FUNCTION_FAILED;
754         }
755         sprintfW( keypath, szUserDataPatch_fmt, usersid, squashed_patch );
756         LocalFree(usersid);
757     }
758     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
759     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
760 }
761 
762 UINT MSIREG_DeleteUserDataPatchKey(LPCWSTR patch, MSIINSTALLCONTEXT context)
763 {
764     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
765     WCHAR *usersid, squashed_patch[SQUASHED_GUID_SIZE], keypath[0x200];
766     HKEY hkey;
767     LONG r;
768 
769     if (!squash_guid( patch, squashed_patch )) return ERROR_FUNCTION_FAILED;
770     TRACE("%s squashed %s\n", debugstr_w(patch), debugstr_w(squashed_patch));
771 
772     if (context == MSIINSTALLCONTEXT_MACHINE)
773         sprintfW(keypath, szUserDataPatches_fmt, szLocalSid);
774     else
775     {
776         if (!(usersid = get_user_sid()))
777         {
778             ERR("Failed to retrieve user SID\n");
779             return ERROR_FUNCTION_FAILED;
780         }
781         sprintfW(keypath, szUserDataPatches_fmt, usersid);
782         LocalFree(usersid);
783     }
784     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, &hkey)) return ERROR_SUCCESS;
785     r = RegDeleteTreeW( hkey, squashed_patch );
786     RegCloseKey(hkey);
787     return r;
788 }
789 
790 UINT MSIREG_OpenUserDataProductPatchesKey(LPCWSTR product, MSIINSTALLCONTEXT context, HKEY *key, BOOL create)
791 {
792     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
793     WCHAR *usersid, squashed_product[SQUASHED_GUID_SIZE], keypath[0x200];
794 
795     if (!squash_guid( product, squashed_product )) return ERROR_FUNCTION_FAILED;
796     TRACE("%s squashed %s\n", debugstr_w(product), debugstr_w(squashed_product));
797 
798     if (context == MSIINSTALLCONTEXT_MACHINE)
799         sprintfW( keypath, szUserDataProductPatches_fmt, szLocalSid, squashed_product );
800     else
801     {
802         if (!(usersid = get_user_sid()))
803         {
804             ERR("Failed to retrieve user SID\n");
805             return ERROR_FUNCTION_FAILED;
806         }
807         sprintfW( keypath, szUserDataProductPatches_fmt, usersid, squashed_product );
808         LocalFree(usersid);
809     }
810     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
811     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
812 }
813 
814 UINT MSIREG_OpenInstallProps(LPCWSTR szProduct, MSIINSTALLCONTEXT dwContext, LPCWSTR szUserSid, HKEY *key, BOOL create)
815 {
816     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
817     WCHAR *usersid, squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200];
818 
819     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
820     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
821 
822     if (dwContext == MSIINSTALLCONTEXT_MACHINE)
823         sprintfW( keypath, szInstallProperties_fmt, szLocalSid, squashed_pc );
824     else if (szUserSid)
825         sprintfW( keypath, szInstallProperties_fmt, szUserSid, squashed_pc );
826     else
827     {
828         if (!(usersid = get_user_sid()))
829         {
830             ERR("Failed to retrieve user SID\n");
831             return ERROR_FUNCTION_FAILED;
832         }
833         sprintfW( keypath, szInstallProperties_fmt, usersid, squashed_pc );
834         LocalFree(usersid);
835     }
836     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
837     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
838 }
839 
840 UINT MSIREG_DeleteUserDataProductKey(LPCWSTR szProduct, MSIINSTALLCONTEXT context)
841 {
842     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
843     WCHAR *usersid, squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200];
844     HKEY hkey;
845     LONG r;
846 
847     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
848     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
849 
850     if (context == MSIINSTALLCONTEXT_MACHINE)
851         sprintfW(keypath, szUserDataProducts_fmt, szLocalSid);
852     else
853     {
854         if (!(usersid = get_user_sid()))
855         {
856             ERR("Failed to retrieve user SID\n");
857             return ERROR_FUNCTION_FAILED;
858         }
859         sprintfW(keypath, szUserDataProducts_fmt, usersid);
860         LocalFree(usersid);
861     }
862 
863     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, &hkey)) return ERROR_SUCCESS;
864     r = RegDeleteTreeW( hkey, squashed_pc );
865     RegCloseKey(hkey);
866     return r;
867 }
868 
869 UINT MSIREG_DeleteProductKey(LPCWSTR szProduct)
870 {
871     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
872     WCHAR squashed_pc[SQUASHED_GUID_SIZE];
873     HKEY hkey;
874     LONG r;
875 
876     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
877     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
878 
879     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szInstaller_Products, 0, access, &hkey)) return ERROR_SUCCESS;
880     r = RegDeleteTreeW( hkey, squashed_pc );
881     RegCloseKey(hkey);
882     return r;
883 }
884 
885 UINT MSIREG_OpenPatchesKey(LPCWSTR szPatch, HKEY *key, BOOL create)
886 {
887     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
888     WCHAR squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200];
889 
890     if (!squash_guid( szPatch, squashed_pc )) return ERROR_FUNCTION_FAILED;
891     TRACE("%s squashed %s\n", debugstr_w(szPatch), debugstr_w(squashed_pc));
892 
893     sprintfW( keypath, szInstaller_Patches, squashed_pc );
894 
895     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
896     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
897 }
898 
899 UINT MSIREG_OpenUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY *key, BOOL create)
900 {
901     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
902     WCHAR squashed_uc[SQUASHED_GUID_SIZE], keypath[0x200];
903 
904     if (!squash_guid( szUpgradeCode, squashed_uc )) return ERROR_FUNCTION_FAILED;
905     TRACE("%s squashed %s\n", debugstr_w(szUpgradeCode), debugstr_w(squashed_uc));
906 
907     strcpyW(keypath, szInstaller_UpgradeCodes);
908     strcatW( keypath, squashed_uc );
909 
910     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
911     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
912 }
913 
914 UINT MSIREG_OpenUserUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create)
915 {
916     WCHAR squashed_uc[SQUASHED_GUID_SIZE], keypath[0x200];
917 
918     if (!squash_guid( szUpgradeCode, squashed_uc )) return ERROR_FUNCTION_FAILED;
919     TRACE("%s squashed %s\n", debugstr_w(szUpgradeCode), debugstr_w(squashed_uc));
920 
921     strcpyW(keypath, szInstaller_UserUpgradeCodes);
922     strcatW( keypath, squashed_uc );
923 
924     if (create) return RegCreateKeyW(HKEY_CURRENT_USER, keypath, key);
925     return RegOpenKeyW(HKEY_CURRENT_USER, keypath, key);
926 }
927 
928 UINT MSIREG_DeleteUpgradeCodesKey( const WCHAR *code )
929 {
930     WCHAR squashed_code[SQUASHED_GUID_SIZE];
931     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
932     HKEY hkey;
933     LONG ret;
934 
935     if (!squash_guid( code, squashed_code )) return ERROR_FUNCTION_FAILED;
936     TRACE( "%s squashed %s\n", debugstr_w(code), debugstr_w(squashed_code) );
937 
938     if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, szInstaller_UpgradeCodes, 0, access, &hkey )) return ERROR_SUCCESS;
939     ret = RegDeleteTreeW( hkey, squashed_code );
940     RegCloseKey( hkey );
941     return ret;
942 }
943 
944 UINT MSIREG_DeleteUserUpgradeCodesKey(LPCWSTR szUpgradeCode)
945 {
946     WCHAR squashed_uc[SQUASHED_GUID_SIZE], keypath[0x200];
947 
948     if (!squash_guid( szUpgradeCode, squashed_uc )) return ERROR_FUNCTION_FAILED;
949     TRACE("%s squashed %s\n", debugstr_w(szUpgradeCode), debugstr_w(squashed_uc));
950 
951     strcpyW(keypath, szInstaller_UserUpgradeCodes);
952     strcatW( keypath, squashed_uc );
953     return RegDeleteTreeW(HKEY_CURRENT_USER, keypath);
954 }
955 
956 UINT MSIREG_DeleteLocalClassesProductKey(LPCWSTR szProductCode)
957 {
958     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
959     WCHAR squashed_pc[SQUASHED_GUID_SIZE];
960     HKEY hkey;
961     LONG r;
962 
963     if (!squash_guid( szProductCode, squashed_pc )) return ERROR_FUNCTION_FAILED;
964     TRACE("%s squashed %s\n", debugstr_w(szProductCode), debugstr_w(squashed_pc));
965 
966     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szInstaller_LocalClassesProducts, 0, access, &hkey)) return ERROR_SUCCESS;
967     r = RegDeleteTreeW( hkey, squashed_pc );
968     RegCloseKey(hkey);
969     return r;
970 }
971 
972 UINT MSIREG_DeleteLocalClassesFeaturesKey(LPCWSTR szProductCode)
973 {
974     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
975     WCHAR squashed_pc[SQUASHED_GUID_SIZE];
976     HKEY hkey;
977     LONG r;
978 
979     if (!squash_guid( szProductCode, squashed_pc )) return ERROR_FUNCTION_FAILED;
980     TRACE("%s squashed %s\n", debugstr_w(szProductCode), debugstr_w(squashed_pc));
981 
982     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szInstaller_LocalClassesFeatures, 0, access, &hkey)) return ERROR_SUCCESS;
983     r = RegDeleteTreeW( hkey, squashed_pc );
984     RegCloseKey(hkey);
985     return r;
986 }
987 
988 UINT MSIREG_OpenClassesUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY *key, BOOL create)
989 {
990     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
991     WCHAR squashed_uc[SQUASHED_GUID_SIZE], keypath[0x200];
992 
993     if (!squash_guid( szUpgradeCode, squashed_uc )) return ERROR_FUNCTION_FAILED;
994     TRACE("%s squashed %s\n", debugstr_w(szUpgradeCode), debugstr_w(squashed_uc));
995 
996     strcpyW(keypath, szInstaller_ClassesUpgradeCode);
997     strcatW( keypath, squashed_uc );
998 
999     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
1000     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
1001 }
1002 
1003 UINT MSIREG_DeleteClassesUpgradeCodesKey(LPCWSTR szUpgradeCode)
1004 {
1005     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
1006     WCHAR squashed_uc[SQUASHED_GUID_SIZE];
1007     HKEY hkey;
1008     LONG r;
1009 
1010     if (!squash_guid( szUpgradeCode, squashed_uc )) return ERROR_FUNCTION_FAILED;
1011     TRACE("%s squashed %s\n", debugstr_w(szUpgradeCode), debugstr_w(squashed_uc));
1012 
1013     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szInstaller_ClassesUpgradeCodes, 0, access, &hkey)) return ERROR_SUCCESS;
1014     r = RegDeleteTreeW( hkey, squashed_uc );
1015     RegCloseKey(hkey);
1016     return r;
1017 }
1018 
1019 /*************************************************************************
1020  *  MsiDecomposeDescriptorW   [MSI.@]
1021  *
1022  * Decomposes an MSI descriptor into product, feature and component parts.
1023  * An MSI descriptor is a string of the form:
1024  *   [base 85 guid] [feature code] '>' [base 85 guid] or
1025  *   [base 85 guid] [feature code] '<'
1026  *
1027  * PARAMS
1028  *   szDescriptor  [I]  the descriptor to decompose
1029  *   szProduct     [O]  buffer of MAX_FEATURE_CHARS+1 for the product guid
1030  *   szFeature     [O]  buffer of MAX_FEATURE_CHARS+1 for the feature code
1031  *   szComponent   [O]  buffer of MAX_FEATURE_CHARS+1 for the component guid
1032  *   pUsed         [O]  the length of the descriptor
1033  *
1034  * RETURNS
1035  *   ERROR_SUCCESS             if everything worked correctly
1036  *   ERROR_INVALID_PARAMETER   if the descriptor was invalid
1037  *
1038  */
1039 UINT WINAPI MsiDecomposeDescriptorW( LPCWSTR szDescriptor, LPWSTR szProduct,
1040                 LPWSTR szFeature, LPWSTR szComponent, LPDWORD pUsed )
1041 {
1042     UINT len;
1043     const WCHAR *p;
1044     GUID product, component;
1045 
1046     TRACE("%s %p %p %p %p\n", debugstr_w(szDescriptor), szProduct,
1047           szFeature, szComponent, pUsed);
1048 
1049     if (!decode_base85_guid( szDescriptor, &product ))
1050         return ERROR_INVALID_PARAMETER;
1051 
1052     TRACE("product %s\n", debugstr_guid( &product ));
1053 
1054     if (!(p = strchrW( &szDescriptor[20], '>' )))
1055         p = strchrW( &szDescriptor[20], '<' );
1056     if (!p)
1057         return ERROR_INVALID_PARAMETER;
1058 
1059     len = (p - &szDescriptor[20]);
1060     if( len > MAX_FEATURE_CHARS )
1061         return ERROR_INVALID_PARAMETER;
1062 
1063     TRACE("feature %s\n", debugstr_wn( &szDescriptor[20], len ));
1064 
1065     if (*p == '>')
1066     {
1067         if (!decode_base85_guid( p+1, &component ))
1068             return ERROR_INVALID_PARAMETER;
1069         TRACE( "component %s\n", debugstr_guid(&component) );
1070     }
1071 
1072     if (szProduct)
1073         StringFromGUID2( &product, szProduct, MAX_FEATURE_CHARS+1 );
1074     if (szComponent)
1075     {
1076         if (*p == '>')
1077             StringFromGUID2( &component, szComponent, MAX_FEATURE_CHARS+1 );
1078         else
1079             szComponent[0] = 0;
1080     }
1081     if (szFeature)
1082     {
1083         memcpy( szFeature, &szDescriptor[20], len*sizeof(WCHAR) );
1084         szFeature[len] = 0;
1085     }
1086 
1087     len = p - szDescriptor + 1;
1088     if (*p == '>') len += 20;
1089 
1090     TRACE("length = %d\n", len);
1091     if (pUsed) *pUsed = len;
1092 
1093     return ERROR_SUCCESS;
1094 }
1095 
1096 UINT WINAPI MsiDecomposeDescriptorA( LPCSTR szDescriptor, LPSTR szProduct,
1097                 LPSTR szFeature, LPSTR szComponent, LPDWORD pUsed )
1098 {
1099     WCHAR product[MAX_FEATURE_CHARS+1];
1100     WCHAR feature[MAX_FEATURE_CHARS+1];
1101     WCHAR component[MAX_FEATURE_CHARS+1];
1102     LPWSTR str = NULL, p = NULL, f = NULL, c = NULL;
1103     UINT r;
1104 
1105     TRACE("%s %p %p %p %p\n", debugstr_a(szDescriptor), szProduct,
1106           szFeature, szComponent, pUsed);
1107 
1108     str = strdupAtoW( szDescriptor );
1109     if( szDescriptor && !str )
1110         return ERROR_OUTOFMEMORY;
1111 
1112     if (szProduct)
1113         p = product;
1114     if (szFeature)
1115         f = feature;
1116     if (szComponent)
1117         c = component;
1118 
1119     r = MsiDecomposeDescriptorW( str, p, f, c, pUsed );
1120 
1121     if (r == ERROR_SUCCESS)
1122     {
1123         WideCharToMultiByte( CP_ACP, 0, p, -1,
1124                              szProduct, MAX_FEATURE_CHARS+1, NULL, NULL );
1125         WideCharToMultiByte( CP_ACP, 0, f, -1,
1126                              szFeature, MAX_FEATURE_CHARS+1, NULL, NULL );
1127         WideCharToMultiByte( CP_ACP, 0, c, -1,
1128                              szComponent, MAX_FEATURE_CHARS+1, NULL, NULL );
1129     }
1130 
1131     msi_free( str );
1132 
1133     return r;
1134 }
1135 
1136 UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid)
1137 {
1138     DWORD r;
1139     WCHAR szwGuid[GUID_SIZE];
1140 
1141     TRACE("%d %p\n", index, lpguid);
1142 
1143     if (NULL == lpguid)
1144         return ERROR_INVALID_PARAMETER;
1145     r = MsiEnumProductsW(index, szwGuid);
1146     if( r == ERROR_SUCCESS )
1147         WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
1148 
1149     return r;
1150 }
1151 
1152 UINT WINAPI MsiEnumProductsW(DWORD index, LPWSTR lpguid)
1153 {
1154     TRACE("%d %p\n", index, lpguid);
1155 
1156     if (NULL == lpguid)
1157         return ERROR_INVALID_PARAMETER;
1158 
1159     return MsiEnumProductsExW( NULL, szAllSid, MSIINSTALLCONTEXT_ALL, index, lpguid,
1160                                NULL, NULL, NULL );
1161 }
1162 
1163 UINT WINAPI MsiEnumFeaturesA(LPCSTR szProduct, DWORD index,
1164       LPSTR szFeature, LPSTR szParent)
1165 {
1166     DWORD r;
1167     WCHAR szwFeature[GUID_SIZE], szwParent[GUID_SIZE];
1168     LPWSTR szwProduct = NULL;
1169 
1170     TRACE("%s %d %p %p\n", debugstr_a(szProduct), index, szFeature, szParent);
1171 
1172     if( szProduct )
1173     {
1174         szwProduct = strdupAtoW( szProduct );
1175         if( !szwProduct )
1176             return ERROR_OUTOFMEMORY;
1177     }
1178 
1179     r = MsiEnumFeaturesW(szwProduct, index, szwFeature, szwParent);
1180     if( r == ERROR_SUCCESS )
1181     {
1182         WideCharToMultiByte(CP_ACP, 0, szwFeature, -1,
1183                             szFeature, GUID_SIZE, NULL, NULL);
1184         WideCharToMultiByte(CP_ACP, 0, szwParent, -1,
1185                             szParent, GUID_SIZE, NULL, NULL);
1186     }
1187 
1188     msi_free( szwProduct);
1189 
1190     return r;
1191 }
1192 
1193 UINT WINAPI MsiEnumFeaturesW(LPCWSTR szProduct, DWORD index,
1194       LPWSTR szFeature, LPWSTR szParent)
1195 {
1196     HKEY hkeyProduct = 0;
1197     DWORD r, sz;
1198 
1199     TRACE("%s %d %p %p\n", debugstr_w(szProduct), index, szFeature, szParent);
1200 
1201     if( !szProduct )
1202         return ERROR_INVALID_PARAMETER;
1203 
1204     r = MSIREG_OpenInstallerFeaturesKey(szProduct,&hkeyProduct,FALSE);
1205     if( r != ERROR_SUCCESS )
1206         return ERROR_NO_MORE_ITEMS;
1207 
1208     sz = GUID_SIZE;
1209     r = RegEnumValueW(hkeyProduct, index, szFeature, &sz, NULL, NULL, NULL, NULL);
1210     RegCloseKey(hkeyProduct);
1211 
1212     return r;
1213 }
1214 
1215 UINT WINAPI MsiEnumComponentsA(DWORD index, LPSTR lpguid)
1216 {
1217     DWORD r;
1218     WCHAR szwGuid[GUID_SIZE];
1219 
1220     TRACE("%u, %p\n", index, lpguid);
1221 
1222     if (!lpguid) return ERROR_INVALID_PARAMETER;
1223 
1224     r = MsiEnumComponentsW(index, szwGuid);
1225     if( r == ERROR_SUCCESS )
1226         WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
1227 
1228     return r;
1229 }
1230 
1231 UINT WINAPI MsiEnumComponentsW(DWORD index, LPWSTR lpguid)
1232 {
1233     TRACE("%u, %p\n", index, lpguid);
1234 
1235     if (!lpguid) return ERROR_INVALID_PARAMETER;
1236 
1237     return MsiEnumComponentsExW( szAllSid, MSIINSTALLCONTEXT_ALL, index, lpguid, NULL, NULL, NULL );
1238 }
1239 
1240 UINT WINAPI MsiEnumComponentsExA( LPCSTR user_sid, DWORD ctx, DWORD index, CHAR guid[39],
1241                                   MSIINSTALLCONTEXT *installed_ctx, LPSTR sid, LPDWORD sid_len )
1242 {
1243     UINT r;
1244     WCHAR *user_sidW = NULL, *sidW = NULL, guidW[GUID_SIZE];
1245 
1246     TRACE("%s, %u, %u, %p, %p, %p, %p\n", debugstr_a(user_sid), ctx, index, guid, installed_ctx,
1247           sid, sid_len);
1248 
1249     if (sid && !sid_len) return ERROR_INVALID_PARAMETER;
1250     if (user_sid && !(user_sidW = strdupAtoW( user_sid ))) return ERROR_OUTOFMEMORY;
1251     if (sid && !(sidW = msi_alloc( *sid_len * sizeof(WCHAR) )))
1252     {
1253         msi_free( user_sidW );
1254         return ERROR_OUTOFMEMORY;
1255     }
1256     r = MsiEnumComponentsExW( user_sidW, ctx, index, guidW, installed_ctx, sidW, sid_len );
1257     if (r == ERROR_SUCCESS)
1258     {
1259         if (guid) WideCharToMultiByte( CP_ACP, 0, guidW, GUID_SIZE, guid, GUID_SIZE, NULL, NULL );
1260         if (sid) WideCharToMultiByte( CP_ACP, 0, sidW, *sid_len + 1, sid, *sid_len + 1, NULL, NULL );
1261     }
1262     msi_free( user_sidW );
1263     msi_free( sidW );
1264     return r;
1265 }
1266 
1267 static UINT fetch_machine_component( DWORD ctx, DWORD index, DWORD *idx, WCHAR guid[39],
1268                                      MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid, LPDWORD sid_len )
1269 {
1270     static const WCHAR componentsW[] =
1271         {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
1272          'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1273          'I','n','s','t','a','l','l','e','r','\\','U','s','e','r','D','a','t','a','\\',
1274          'S','-','1','-','5','-','1','8','\\','C','o','m','p','o','n','e','n','t','s',0};
1275     UINT r = ERROR_SUCCESS;
1276     WCHAR component[SQUASHED_GUID_SIZE];
1277     DWORD i = 0, len_component;
1278     REGSAM access = KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY;
1279     HKEY key_components;
1280 
1281     if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, componentsW, 0, access, &key_components ))
1282         return ERROR_NO_MORE_ITEMS;
1283 
1284     len_component = sizeof(component)/sizeof(component[0]);
1285     while (!RegEnumKeyExW( key_components, i, component, &len_component, NULL, NULL, NULL, NULL ))
1286     {
1287         if (*idx == index) goto found;
1288         (*idx)++;
1289         len_component = sizeof(component)/sizeof(component[0]);
1290         i++;
1291     }
1292     RegCloseKey( key_components );
1293     return ERROR_NO_MORE_ITEMS;
1294 
1295 found:
1296     if (sid_len)
1297     {
1298         if (*sid_len < 1)
1299         {
1300             *sid_len = 1;
1301             r = ERROR_MORE_DATA;
1302         }
1303         else if (sid)
1304         {
1305             *sid_len = 0;
1306             sid[0] = 0;
1307         }
1308     }
1309     if (guid) unsquash_guid( component, guid );
1310     if (installed_ctx) *installed_ctx = MSIINSTALLCONTEXT_MACHINE;
1311     RegCloseKey( key_components );
1312     return r;
1313 }
1314 
1315 static UINT fetch_user_component( const WCHAR *usersid, DWORD ctx, DWORD index, DWORD *idx,
1316                                   WCHAR guid[39], MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid,
1317                                   LPDWORD sid_len )
1318 {
1319     static const WCHAR userdataW[] =
1320         {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
1321          'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1322          'I','n','s','t','a','l','l','e','r','\\','U','s','e','r','D','a','t','a',0};
1323     static const WCHAR componentsW[] = {'\\','C','o','m','p','o','n','e','n','t','s',0};
1324     UINT r = ERROR_SUCCESS;
1325     WCHAR path[MAX_PATH], component[SQUASHED_GUID_SIZE], user[128];
1326     DWORD i = 0, j = 0, len_component, len_user;
1327     REGSAM access = KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY;
1328     HKEY key_users, key_components;
1329 
1330     if (ctx == MSIINSTALLCONTEXT_USERMANAGED) /* FIXME: where to find these? */
1331         return ERROR_NO_MORE_ITEMS;
1332 
1333     if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, userdataW, 0, access, &key_users ))
1334         return ERROR_NO_MORE_ITEMS;
1335 
1336     len_user = sizeof(user)/sizeof(user[0]);
1337     while (!RegEnumKeyExW( key_users, i, user, &len_user, NULL, NULL, NULL, NULL ))
1338     {
1339         if ((strcmpW( usersid, szAllSid ) && strcmpW( usersid, user )) ||
1340             !strcmpW( szLocalSid, user ))
1341         {
1342             i++;
1343             len_user = sizeof(user)/sizeof(user[0]);
1344             continue;
1345         }
1346         strcpyW( path, user );
1347         strcatW( path, componentsW );
1348         if (RegOpenKeyExW( key_users, path, 0, access, &key_components ))
1349         {
1350             i++;
1351             len_user = sizeof(user)/sizeof(user[0]);
1352             continue;
1353         }
1354         len_component = sizeof(component)/sizeof(component[0]);
1355         while (!RegEnumKeyExW( key_components, j, component, &len_component, NULL, NULL, NULL, NULL ))
1356         {
1357             if (*idx == index) goto found;
1358             (*idx)++;
1359             len_component = sizeof(component)/sizeof(component[0]);
1360             j++;
1361         }
1362         RegCloseKey( key_components );
1363         len_user = sizeof(user)/sizeof(user[0]);
1364         i++;
1365     }
1366     RegCloseKey( key_users );
1367     return ERROR_NO_MORE_ITEMS;
1368 
1369 found:
1370     if (sid_len)
1371     {
1372         if (*sid_len < len_user + 1)
1373         {
1374             *sid_len = len_user + 1;
1375             r = ERROR_MORE_DATA;
1376         }
1377         else if (sid)
1378         {
1379             *sid_len = len_user;
1380             strcpyW( sid, user );
1381         }
1382     }
1383     if (guid) unsquash_guid( component, guid );
1384     if (installed_ctx) *installed_ctx = ctx;
1385     RegCloseKey( key_components );
1386     RegCloseKey( key_users );
1387     return r;
1388 }
1389 
1390 static UINT enum_components( const WCHAR *usersid, DWORD ctx, DWORD index, DWORD *idx, WCHAR guid[39],
1391                              MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid, LPDWORD sid_len )
1392 {
1393     UINT r = ERROR_NO_MORE_ITEMS;
1394     WCHAR *user = NULL;
1395 
1396     if (!usersid)
1397     {
1398         usersid = user = get_user_sid();
1399         if (!user) return ERROR_FUNCTION_FAILED;
1400     }
1401     if (ctx & MSIINSTALLCONTEXT_USERMANAGED)
1402     {
1403         r = fetch_user_component( usersid, MSIINSTALLCONTEXT_USERMANAGED, index, idx, guid,
1404                                   installed_ctx, sid, sid_len );
1405         if (r != ERROR_NO_MORE_ITEMS) goto done;
1406     }
1407     if (ctx & MSIINSTALLCONTEXT_USERUNMANAGED)
1408     {
1409         r = fetch_user_component( usersid, MSIINSTALLCONTEXT_USERUNMANAGED, index, idx, guid,
1410                                   installed_ctx, sid, sid_len );
1411         if (r != ERROR_NO_MORE_ITEMS) goto done;
1412     }
1413     if (ctx & MSIINSTALLCONTEXT_MACHINE)
1414     {
1415         r = fetch_machine_component( MSIINSTALLCONTEXT_MACHINE, index, idx, guid, installed_ctx,
1416                                      sid, sid_len );
1417         if (r != ERROR_NO_MORE_ITEMS) goto done;
1418     }
1419 
1420 done:
1421     LocalFree( user );
1422     return r;
1423 }
1424 
1425 UINT WINAPI MsiEnumComponentsExW( LPCWSTR user_sid, DWORD ctx, DWORD index, WCHAR guid[39],
1426                                   MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid, LPDWORD sid_len )
1427 {
1428     UINT r;
1429     DWORD idx = 0;
1430     static DWORD last_index;
1431 
1432     TRACE("%s, %u, %u, %p, %p, %p, %p\n", debugstr_w(user_sid), ctx, index, guid, installed_ctx,
1433           sid, sid_len);
1434 
1435     if ((sid && !sid_len) || !ctx || (user_sid && ctx == MSIINSTALLCONTEXT_MACHINE))
1436         return ERROR_INVALID_PARAMETER;
1437 
1438     if (index && index - last_index != 1)
1439         return ERROR_INVALID_PARAMETER;
1440 
1441     if (!index) last_index = 0;
1442 
1443     r = enum_components( user_sid, ctx, index, &idx, guid, installed_ctx, sid, sid_len );
1444     if (r == ERROR_SUCCESS)
1445         last_index = index;
1446     else
1447         last_index = 0;
1448 
1449     return r;
1450 }
1451 
1452 UINT WINAPI MsiEnumClientsA(LPCSTR szComponent, DWORD index, LPSTR szProduct)
1453 {
1454     DWORD r;
1455     WCHAR szwProduct[GUID_SIZE];
1456     LPWSTR szwComponent = NULL;
1457 
1458     TRACE("%s %d %p\n", debugstr_a(szComponent), index, szProduct);
1459 
1460     if ( !szProduct )
1461         return ERROR_INVALID_PARAMETER;
1462 
1463     if( szComponent )
1464     {
1465         szwComponent = strdupAtoW( szComponent );
1466         if( !szwComponent )
1467             return ERROR_OUTOFMEMORY;
1468     }
1469 
1470     r = MsiEnumClientsW(szComponent?szwComponent:NULL, index, szwProduct);
1471     if( r == ERROR_SUCCESS )
1472     {
1473         WideCharToMultiByte(CP_ACP, 0, szwProduct, -1,
1474                             szProduct, GUID_SIZE, NULL, NULL);
1475     }
1476 
1477     msi_free( szwComponent);
1478 
1479     return r;
1480 }
1481 
1482 UINT WINAPI MsiEnumClientsW(LPCWSTR szComponent, DWORD index, LPWSTR szProduct)
1483 {
1484     HKEY hkeyComp = 0;
1485     DWORD r, sz;
1486     WCHAR szValName[SQUASHED_GUID_SIZE];
1487 
1488     TRACE("%s %d %p\n", debugstr_w(szComponent), index, szProduct);
1489 
1490     if (!szComponent || !*szComponent || !szProduct)
1491         return ERROR_INVALID_PARAMETER;
1492 
1493     if (MSIREG_OpenUserDataComponentKey(szComponent, NULL, &hkeyComp, FALSE) != ERROR_SUCCESS &&
1494         MSIREG_OpenUserDataComponentKey(szComponent, szLocalSid, &hkeyComp, FALSE) != ERROR_SUCCESS)
1495         return ERROR_UNKNOWN_COMPONENT;
1496 
1497     /* see if there are any products at all */
1498     sz = SQUASHED_GUID_SIZE;
1499     r = RegEnumValueW(hkeyComp, 0, szValName, &sz, NULL, NULL, NULL, NULL);
1500     if (r != ERROR_SUCCESS)
1501     {
1502         RegCloseKey(hkeyComp);
1503 
1504         if (index != 0)
1505             return ERROR_INVALID_PARAMETER;
1506 
1507         return ERROR_UNKNOWN_COMPONENT;
1508     }
1509 
1510     sz = SQUASHED_GUID_SIZE;
1511     r = RegEnumValueW(hkeyComp, index, szValName, &sz, NULL, NULL, NULL, NULL);
1512     if( r == ERROR_SUCCESS )
1513     {
1514         unsquash_guid(szValName, szProduct);
1515         TRACE("-> %s\n", debugstr_w(szProduct));
1516     }
1517     RegCloseKey(hkeyComp);
1518     return r;
1519 }
1520 
1521 UINT WINAPI MsiEnumClientsExA(LPCSTR component, LPCSTR usersid, DWORD ctx, DWORD index,
1522                               CHAR installed_product[GUID_SIZE],
1523                               MSIINSTALLCONTEXT *installed_ctx, LPSTR sid, LPDWORD sid_len)
1524 {
1525     FIXME("%s, %s, %u, %u, %p, %p, %p, %p\n", debugstr_a(component), debugstr_a(usersid),
1526           ctx, index, installed_product, installed_ctx, sid, sid_len);
1527     return ERROR_ACCESS_DENIED;
1528 }
1529 
1530 UINT WINAPI MsiEnumClientsExW(LPCWSTR component, LPCWSTR usersid, DWORD ctx, DWORD index,
1531                               WCHAR installed_product[GUID_SIZE],
1532                               MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid, LPDWORD sid_len)
1533 {
1534     FIXME("%s, %s, %u, %u, %p, %p, %p, %p\n", debugstr_w(component), debugstr_w(usersid),
1535           ctx, index, installed_product, installed_ctx, sid, sid_len);
1536     return ERROR_ACCESS_DENIED;
1537 }
1538 
1539 static UINT MSI_EnumComponentQualifiers( LPCWSTR szComponent, DWORD iIndex,
1540                 awstring *lpQualBuf, LPDWORD pcchQual,
1541                 awstring *lpAppBuf, LPDWORD pcchAppBuf )
1542 {
1543     DWORD name_sz, val_sz, name_max, val_max, type, ofs;
1544     LPWSTR name = NULL, val = NULL;
1545     UINT r, r2;
1546     HKEY key;
1547 
1548     TRACE("%s %08x %p %p %p %p\n", debugstr_w(szComponent), iIndex,
1549           lpQualBuf, pcchQual, lpAppBuf, pcchAppBuf);
1550 
1551     if (!szComponent)
1552         return ERROR_INVALID_PARAMETER;
1553 
1554     r = MSIREG_OpenUserComponentsKey( szComponent, &key, FALSE );
1555     if (r != ERROR_SUCCESS)
1556         return ERROR_UNKNOWN_COMPONENT;
1557 
1558     /* figure out how big the name is we want to return */
1559     name_max = 0x10;
1560     r = ERROR_OUTOFMEMORY;
1561     name = msi_alloc( name_max * sizeof(WCHAR) );
1562     if (!name)
1563         goto end;
1564 
1565     val_max = 0x10;
1566     r = ERROR_OUTOFMEMORY;
1567     val = msi_alloc( val_max );
1568     if (!val)
1569         goto end;
1570 
1571     /* loop until we allocate enough memory */
1572     while (1)
1573     {
1574         name_sz = name_max;
1575         val_sz = val_max;
1576         r = RegEnumValueW( key, iIndex, name, &name_sz,
1577                            NULL, &type, (LPBYTE)val, &val_sz );
1578         if (r == ERROR_SUCCESS)
1579             break;
1580         if (r != ERROR_MORE_DATA)
1581             goto end;
1582 
1583         if (type != REG_MULTI_SZ)
1584         {
1585             ERR("component data has wrong type (%d)\n", type);
1586             goto end;
1587         }
1588 
1589         r = ERROR_OUTOFMEMORY;
1590         if (name_sz + 1 >= name_max)
1591         {
1592             name_max *= 2;
1593             msi_free( name );
1594             name = msi_alloc( name_max * sizeof (WCHAR) );
1595             if (!name)
1596                 goto end;
1597             continue;
1598         }
1599         if (val_sz > val_max)
1600         {
1601             val_max = val_sz + sizeof (WCHAR);
1602             msi_free( val );
1603             val = msi_alloc( val_max * sizeof (WCHAR) );
1604             if (!val)
1605                 goto end;
1606             continue;
1607         }
1608         ERR("should be enough data, but isn't %d %d\n", name_sz, val_sz );
1609         goto end;
1610     }
1611 
1612     ofs = 0;
1613     r = MsiDecomposeDescriptorW( val, NULL, NULL, NULL, &ofs );
1614     if (r != ERROR_SUCCESS)
1615         goto end;
1616 
1617     TRACE("Providing %s and %s\n", debugstr_w(name), debugstr_w(val+ofs));
1618 
1619     r = msi_strcpy_to_awstring( name, -1, lpQualBuf, pcchQual );
1620     r2 = msi_strcpy_to_awstring( val+ofs, -1, lpAppBuf, pcchAppBuf );
1621 
1622     if (r2 != ERROR_SUCCESS)
1623         r = r2;
1624 
1625 end:
1626     msi_free(val);
1627     msi_free(name);
1628     RegCloseKey(key);
1629     return r;
1630 }
1631 
1632 /*************************************************************************
1633  *  MsiEnumComponentQualifiersA [MSI.@]
1634  */
1635 UINT WINAPI MsiEnumComponentQualifiersA( LPCSTR szComponent, DWORD iIndex,
1636                 LPSTR lpQualifierBuf, LPDWORD pcchQualifierBuf,
1637                 LPSTR lpApplicationDataBuf, LPDWORD pcchApplicationDataBuf )
1638 {
1639     awstring qual, appdata;
1640     LPWSTR comp;
1641     UINT r;
1642 
1643     TRACE("%s %08x %p %p %p %p\n", debugstr_a(szComponent), iIndex,
1644           lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf,
1645           pcchApplicationDataBuf);
1646 
1647     comp = strdupAtoW( szComponent );
1648     if (szComponent && !comp)
1649         return ERROR_OUTOFMEMORY;
1650 
1651     qual.unicode = FALSE;
1652     qual.str.a = lpQualifierBuf;
1653 
1654     appdata.unicode = FALSE;
1655     appdata.str.a = lpApplicationDataBuf;
1656 
1657     r = MSI_EnumComponentQualifiers( comp, iIndex,
1658               &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf );
1659     msi_free( comp );
1660     return r;
1661 }
1662 
1663 /*************************************************************************
1664  *  MsiEnumComponentQualifiersW [MSI.@]
1665  */
1666 UINT WINAPI MsiEnumComponentQualifiersW( LPCWSTR szComponent, DWORD iIndex,
1667                 LPWSTR lpQualifierBuf, LPDWORD pcchQualifierBuf,
1668                 LPWSTR lpApplicationDataBuf, LPDWORD pcchApplicationDataBuf )
1669 {
1670     awstring qual, appdata;
1671 
1672     TRACE("%s %08x %p %p %p %p\n", debugstr_w(szComponent), iIndex,
1673           lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf,
1674           pcchApplicationDataBuf);
1675 
1676     qual.unicode = TRUE;
1677     qual.str.w = lpQualifierBuf;
1678 
1679     appdata.unicode = TRUE;
1680     appdata.str.w = lpApplicationDataBuf;
1681 
1682     return MSI_EnumComponentQualifiers( szComponent, iIndex,
1683                  &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf );
1684 }
1685 
1686 /*************************************************************************
1687  *  MsiEnumRelatedProductsW   [MSI.@]
1688  *
1689  */
1690 UINT WINAPI MsiEnumRelatedProductsW(LPCWSTR szUpgradeCode, DWORD dwReserved,
1691                                     DWORD iProductIndex, LPWSTR lpProductBuf)
1692 {
1693     UINT r;
1694     HKEY hkey;
1695     WCHAR szKeyName[SQUASHED_GUID_SIZE];
1696     DWORD dwSize = sizeof(szKeyName)/sizeof(szKeyName[0]);
1697 
1698     TRACE("%s %u %u %p\n", debugstr_w(szUpgradeCode), dwReserved,
1699           iProductIndex, lpProductBuf);
1700 
1701     if (NULL == szUpgradeCode)
1702         return ERROR_INVALID_PARAMETER;
1703     if (NULL == lpProductBuf)
1704         return ERROR_INVALID_PARAMETER;
1705 
1706     r = MSIREG_OpenUpgradeCodesKey(szUpgradeCode, &hkey, FALSE);
1707     if (r != ERROR_SUCCESS)
1708         return ERROR_NO_MORE_ITEMS;
1709 
1710     r = RegEnumValueW(hkey, iProductIndex, szKeyName, &dwSize, NULL, NULL, NULL, NULL);
1711     if( r == ERROR_SUCCESS )
1712         unsquash_guid(szKeyName, lpProductBuf);
1713     RegCloseKey(hkey);
1714 
1715     return r;
1716 }
1717 
1718 /*************************************************************************
1719  *  MsiEnumRelatedProductsA   [MSI.@]
1720  *
1721  */
1722 UINT WINAPI MsiEnumRelatedProductsA(LPCSTR szUpgradeCode, DWORD dwReserved,
1723                                     DWORD iProductIndex, LPSTR lpProductBuf)
1724 {
1725     LPWSTR szwUpgradeCode = NULL;
1726     WCHAR productW[GUID_SIZE];
1727     UINT r;
1728 
1729     TRACE("%s %u %u %p\n", debugstr_a(szUpgradeCode), dwReserved,
1730           iProductIndex, lpProductBuf);
1731 
1732     if (szUpgradeCode)
1733     {
1734         szwUpgradeCode = strdupAtoW( szUpgradeCode );
1735         if( !szwUpgradeCode )
1736             return ERROR_OUTOFMEMORY;
1737     }
1738 
1739     r = MsiEnumRelatedProductsW( szwUpgradeCode, dwReserved,
1740                                  iProductIndex, productW );
1741     if (r == ERROR_SUCCESS)
1742     {
1743         WideCharToMultiByte( CP_ACP, 0, productW, GUID_SIZE,
1744                              lpProductBuf, GUID_SIZE, NULL, NULL );
1745     }
1746     msi_free( szwUpgradeCode);
1747     return r;
1748 }
1749 
1750 /***********************************************************************
1751  * MsiEnumPatchesExA            [MSI.@]
1752  */
1753 UINT WINAPI MsiEnumPatchesExA(LPCSTR szProductCode, LPCSTR szUserSid,
1754         DWORD dwContext, DWORD dwFilter, DWORD dwIndex, LPSTR szPatchCode,
1755         LPSTR szTargetProductCode, MSIINSTALLCONTEXT *pdwTargetProductContext,
1756         LPSTR szTargetUserSid, LPDWORD pcchTargetUserSid)
1757 {
1758     LPWSTR prodcode = NULL;
1759     LPWSTR usersid = NULL;
1760     LPWSTR targsid = NULL;
1761     WCHAR patch[GUID_SIZE];
1762     WCHAR targprod[GUID_SIZE];
1763     DWORD len;
1764     UINT r;
1765 
1766     TRACE("(%s, %s, %d, %d, %d, %p, %p, %p, %p, %p)\n",
1767           debugstr_a(szProductCode), debugstr_a(szUserSid), dwContext, dwFilter,
1768           dwIndex, szPatchCode, szTargetProductCode, pdwTargetProductContext,
1769           szTargetUserSid, pcchTargetUserSid);
1770 
1771     if (szTargetUserSid && !pcchTargetUserSid)
1772         return ERROR_INVALID_PARAMETER;
1773 
1774     if (szProductCode) prodcode = strdupAtoW(szProductCode);
1775     if (szUserSid) usersid = strdupAtoW(szUserSid);
1776 
1777     r = MsiEnumPatchesExW(prodcode, usersid, dwContext, dwFilter, dwIndex,
1778                           patch, targprod, pdwTargetProductContext,
1779                           NULL, &len);
1780     if (r != ERROR_SUCCESS)
1781         goto done;
1782 
1783     WideCharToMultiByte(CP_ACP, 0, patch, -1, szPatchCode,
1784                         GUID_SIZE, NULL, NULL);
1785     WideCharToMultiByte(CP_ACP, 0, targprod, -1, szTargetProductCode,
1786                         GUID_SIZE, NULL, NULL);
1787 
1788     if (!szTargetUserSid)
1789     {
1790         if (pcchTargetUserSid)
1791             *pcchTargetUserSid = len;
1792 
1793         goto done;
1794     }
1795 
1796     targsid = msi_alloc(++len * sizeof(WCHAR));
1797     if (!targsid)
1798     {
1799         r = ERROR_OUTOFMEMORY;
1800         goto done;
1801     }
1802 
1803     r = MsiEnumPatchesExW(prodcode, usersid, dwContext, dwFilter, dwIndex,
1804                           patch, targprod, pdwTargetProductContext,
1805                           targsid, &len);
1806     if (r != ERROR_SUCCESS || !szTargetUserSid)
1807         goto done;
1808 
1809     WideCharToMultiByte(CP_ACP, 0, targsid, -1, szTargetUserSid,
1810                         *pcchTargetUserSid, NULL, NULL);
1811 
1812     len = lstrlenW(targsid);
1813     if (*pcchTargetUserSid < len + 1)
1814     {
1815         r = ERROR_MORE_DATA;
1816         *pcchTargetUserSid = len * sizeof(WCHAR);
1817     }
1818     else
1819         *pcchTargetUserSid = len;
1820 
1821 done:
1822     msi_free(prodcode);
1823     msi_free(usersid);
1824     msi_free(targsid);
1825 
1826     return r;
1827 }
1828 
1829 static UINT msi_get_patch_state(LPCWSTR prodcode, LPCWSTR usersid,
1830                                 MSIINSTALLCONTEXT context,
1831                                 LPWSTR patch, MSIPATCHSTATE *state)
1832 {
1833     DWORD type, val, size;
1834     HKEY prod, hkey = 0;
1835     HKEY udpatch = 0;
1836     LONG res;
1837     UINT r = ERROR_NO_MORE_ITEMS;
1838 
1839     *state = MSIPATCHSTATE_INVALID;
1840 
1841     r = MSIREG_OpenUserDataProductKey(prodcode, context,
1842                                       usersid, &prod, FALSE);
1843     if (r != ERROR_SUCCESS)
1844         return ERROR_NO_MORE_ITEMS;
1845 
1846     res = RegOpenKeyExW(prod, szPatches, 0, KEY_READ, &hkey);
1847     if (res != ERROR_SUCCESS)
1848         goto done;
1849 
1850     res = RegOpenKeyExW(hkey, patch, 0, KEY_READ, &udpatch);
1851     if (res != ERROR_SUCCESS)
1852         goto done;
1853 
1854     size = sizeof(DWORD);
1855     res = RegGetValueW(udpatch, NULL, szState, RRF_RT_DWORD, &type, &val, &size);
1856     if (res != ERROR_SUCCESS ||
1857         val < MSIPATCHSTATE_APPLIED || val > MSIPATCHSTATE_REGISTERED)
1858     {
1859         r = ERROR_BAD_CONFIGURATION;
1860         goto done;
1861     }
1862 
1863     *state = val;
1864     r = ERROR_SUCCESS;
1865 
1866 done:
1867     RegCloseKey(udpatch);
1868     RegCloseKey(hkey);
1869     RegCloseKey(prod);
1870 
1871     return r;
1872 }
1873 
1874 static UINT msi_check_product_patches(LPCWSTR prodcode, LPCWSTR usersid,
1875         MSIINSTALLCONTEXT context, DWORD filter, DWORD index, DWORD *idx,
1876         LPWSTR patch, LPWSTR targetprod, MSIINSTALLCONTEXT *targetctx,
1877         LPWSTR targetsid, DWORD *sidsize, LPWSTR *transforms)
1878 {
1879     MSIPATCHSTATE state = MSIPATCHSTATE_INVALID;
1880     LPWSTR ptr, patches = NULL;
1881     HKEY prod, patchkey = 0;
1882     HKEY localprod = 0, localpatch = 0;
1883     DWORD type, size;
1884     LONG res;
1885     UINT temp, r = ERROR_NO_MORE_ITEMS;
1886 
1887     if (MSIREG_OpenProductKey(prodcode, usersid, context,
1888                               &prod, FALSE) != ERROR_SUCCESS)
1889         return ERROR_NO_MORE_ITEMS;
1890 
1891     size = 0;
1892     res = RegGetValueW(prod, szPatches, szPatches, RRF_RT_ANY, &type, NULL,
1893                        &size);
1894     if (res != ERROR_SUCCESS)
1895         goto done;
1896 
1897     if (type != REG_MULTI_SZ)
1898     {
1899         r = ERROR_BAD_CONFIGURATION;
1900         goto done;
1901     }
1902 
1903     patches = msi_alloc(size);
1904     if (!patches)
1905     {
1906         r = ERROR_OUTOFMEMORY;
1907         goto done;
1908     }
1909 
1910     res = RegGetValueW(prod, szPatches, szPatches, RRF_RT_ANY, &type,
1911                        patches, &size);
1912     if (res != ERROR_SUCCESS)
1913         goto done;
1914 
1915     for (ptr = patches; *ptr && r == ERROR_NO_MORE_ITEMS; ptr += lstrlenW(ptr) + 1)
1916     {
1917         if (!unsquash_guid(ptr, patch))
1918         {
1919             r = ERROR_BAD_CONFIGURATION;
1920             goto done;
1921         }
1922 
1923         size = 0;
1924         res = RegGetValueW(prod, szPatches, ptr, RRF_RT_REG_SZ,
1925                            &type, NULL, &size);
1926         if (res != ERROR_SUCCESS)
1927             continue;
1928 
1929         if (transforms)
1930         {
1931             *transforms = msi_alloc(size);
1932             if (!*transforms)
1933             {
1934                 r = ERROR_OUTOFMEMORY;
1935                 goto done;
1936             }
1937 
1938             res = RegGetValueW(prod, szPatches, ptr, RRF_RT_REG_SZ,
1939                                &type, *transforms, &size);
1940             if (res != ERROR_SUCCESS)
1941                 continue;
1942         }
1943 
1944         if (context == MSIINSTALLCONTEXT_USERMANAGED)
1945         {
1946             if (!(filter & MSIPATCHSTATE_APPLIED))
1947             {
1948                 temp = msi_get_patch_state(prodcode, usersid, context,
1949                                            ptr, &state);
1950                 if (temp == ERROR_BAD_CONFIGURATION)
1951                 {
1952                     r = ERROR_BAD_CONFIGURATION;
1953                     goto done;
1954                 }
1955 
1956                 if (temp != ERROR_SUCCESS || !(filter & state))
1957                     continue;
1958             }
1959         }
1960         else if (context == MSIINSTALLCONTEXT_USERUNMANAGED)
1961         {
1962             if (!(filter & MSIPATCHSTATE_APPLIED))
1963             {
1964                 temp = msi_get_patch_state(prodcode, usersid, context,
1965                                            ptr, &state);
1966                 if (temp == ERROR_BAD_CONFIGURATION)
1967                 {
1968                     r = ERROR_BAD_CONFIGURATION;
1969                     goto done;
1970                 }
1971 
1972                 if (temp != ERROR_SUCCESS || !(filter & state))
1973                     continue;
1974             }
1975             else
1976             {
1977                 temp = MSIREG_OpenUserDataPatchKey(patch, context,
1978                                                    &patchkey, FALSE);
1979                 RegCloseKey(patchkey);
1980                 if (temp != ERROR_SUCCESS)
1981                     continue;
1982             }
1983         }
1984         else if (context == MSIINSTALLCONTEXT_MACHINE)
1985         {
1986             usersid = szEmpty;
1987 
1988             if (MSIREG_OpenUserDataProductKey(prodcode, context, NULL, &localprod, FALSE) == ERROR_SUCCESS &&
1989                 RegOpenKeyExW(localprod, szPatches, 0, KEY_READ, &localpatch) == ERROR_SUCCESS &&
1990                 RegOpenKeyExW(localpatch, ptr, 0, KEY_READ, &patchkey) == ERROR_SUCCESS)
1991             {
1992                 res = RegGetValueW(patchkey, NULL, szState, RRF_RT_REG_DWORD,
1993                                    &type, &state, &size);
1994 
1995                 if (!(filter & state))
1996                     res = ERROR_NO_MORE_ITEMS;
1997 
1998                 RegCloseKey(patchkey);
1999             }
2000 
2001             RegCloseKey(localpatch);
2002             RegCloseKey(localprod);
2003 
2004             if (res != ERROR_SUCCESS)
2005                 continue;
2006         }
2007 
2008         if (*idx < index)
2009         {
2010             (*idx)++;
2011             continue;
2012         }
2013 
2014         r = ERROR_SUCCESS;
2015         if (targetprod)
2016             lstrcpyW(targetprod, prodcode);
2017 
2018         if (targetctx)
2019             *targetctx = context;
2020 
2021         if (targetsid)
2022         {
2023             lstrcpynW(targetsid, usersid, *sidsize);
2024             if (lstrlenW(usersid) >= *sidsize)
2025                 r = ERROR_MORE_DATA;
2026         }
2027 
2028         if (sidsize)
2029         {
2030             *sidsize = lstrlenW(usersid);
2031             if (!targetsid)
2032                 *sidsize *= sizeof(WCHAR);
2033         }
2034     }
2035 
2036 done:
2037     RegCloseKey(prod);
2038     msi_free(patches);
2039 
2040     return r;
2041 }
2042 
2043 static UINT msi_enum_patches(LPCWSTR szProductCode, LPCWSTR szUserSid,
2044         DWORD dwContext, DWORD dwFilter, DWORD dwIndex, DWORD *idx,
2045         LPWSTR szPatchCode, LPWSTR szTargetProductCode,
2046         MSIINSTALLCONTEXT *pdwTargetProductContext, LPWSTR szTargetUserSid,
2047         LPDWORD pcchTargetUserSid, LPWSTR *szTransforms)
2048 {
2049     LPWSTR usersid = NULL;
2050     UINT r = ERROR_INVALID_PARAMETER;
2051 
2052     if (!szUserSid)
2053     {
2054         szUserSid = usersid = get_user_sid();
2055         if (!usersid) return ERROR_FUNCTION_FAILED;
2056     }
2057 
2058     if (dwContext & MSIINSTALLCONTEXT_USERMANAGED)
2059     {
2060         r = msi_check_product_patches(szProductCode, szUserSid,
2061                                       MSIINSTALLCONTEXT_USERMANAGED, dwFilter,
2062                                       dwIndex, idx, szPatchCode,
2063                                       szTargetProductCode,
2064                                       pdwTargetProductContext, szTargetUserSid,
2065                                       pcchTargetUserSid, szTransforms);
2066         if (r != ERROR_NO_MORE_ITEMS)
2067             goto done;
2068     }
2069 
2070     if (dwContext & MSIINSTALLCONTEXT_USERUNMANAGED)
2071     {
2072         r = msi_check_product_patches(szProductCode, szUserSid,
2073                                       MSIINSTALLCONTEXT_USERUNMANAGED, dwFilter,
2074                                       dwIndex, idx, szPatchCode,
2075                                       szTargetProductCode,
2076                                       pdwTargetProductContext, szTargetUserSid,
2077                                       pcchTargetUserSid, szTransforms);
2078         if (r != ERROR_NO_MORE_ITEMS)
2079             goto done;
2080     }
2081 
2082     if (dwContext & MSIINSTALLCONTEXT_MACHINE)
2083     {
2084         r = msi_check_product_patches(szProductCode, szUserSid,
2085                                       MSIINSTALLCONTEXT_MACHINE, dwFilter,
2086                                       dwIndex, idx, szPatchCode,
2087                                       szTargetProductCode,
2088                                       pdwTargetProductContext, szTargetUserSid,
2089                                       pcchTargetUserSid, szTransforms);
2090         if (r != ERROR_NO_MORE_ITEMS)
2091             goto done;
2092     }
2093 
2094 done:
2095     LocalFree(usersid);
2096     return r;
2097 }
2098 
2099 /***********************************************************************
2100  * MsiEnumPatchesExW            [MSI.@]
2101  */
2102 UINT WINAPI MsiEnumPatchesExW(LPCWSTR szProductCode, LPCWSTR szUserSid,
2103         DWORD dwContext, DWORD dwFilter, DWORD dwIndex, LPWSTR szPatchCode,
2104         LPWSTR szTargetProductCode, MSIINSTALLCONTEXT *pdwTargetProductContext,
2105         LPWSTR szTargetUserSid, LPDWORD pcchTargetUserSid)
2106 {
2107     WCHAR squashed_pc[SQUASHED_GUID_SIZE];
2108     DWORD idx = 0;
2109     UINT r;
2110 
2111     static DWORD last_index;
2112 
2113     TRACE("(%s, %s, %d, %d, %d, %p, %p, %p, %p, %p)\n",
2114           debugstr_w(szProductCode), debugstr_w(szUserSid), dwContext, dwFilter,
2115           dwIndex, szPatchCode, szTargetProductCode, pdwTargetProductContext,
2116           szTargetUserSid, pcchTargetUserSid);
2117 
2118     if (!szProductCode || !squash_guid( szProductCode, squashed_pc ))
2119         return ERROR_INVALID_PARAMETER;
2120 
2121     if (szUserSid && !strcmpW( szUserSid, szLocalSid ))
2122         return ERROR_INVALID_PARAMETER;
2123 
2124     if (dwContext & MSIINSTALLCONTEXT_MACHINE && szUserSid)
2125         return ERROR_INVALID_PARAMETER;
2126 
2127     if (dwContext <= MSIINSTALLCONTEXT_NONE ||
2128         dwContext > MSIINSTALLCONTEXT_ALL)
2129         return ERROR_INVALID_PARAMETER;
2130 
2131     if (dwFilter <= MSIPATCHSTATE_INVALID || dwFilter > MSIPATCHSTATE_ALL)
2132         return ERROR_INVALID_PARAMETER;
2133 
2134     if (dwIndex && dwIndex - last_index != 1)
2135         return ERROR_INVALID_PARAMETER;
2136 
2137     if (dwIndex == 0)
2138         last_index = 0;
2139 
2140     r = msi_enum_patches(szProductCode, szUserSid, dwContext, dwFilter,
2141                          dwIndex, &idx, szPatchCode, szTargetProductCode,
2142                          pdwTargetProductContext, szTargetUserSid,
2143                          pcchTargetUserSid, NULL);
2144 
2145     if (r == ERROR_SUCCESS)
2146         last_index = dwIndex;
2147     else
2148         last_index = 0;
2149 
2150     return r;
2151 }
2152 
2153 /***********************************************************************
2154  * MsiEnumPatchesA            [MSI.@]
2155  */
2156 UINT WINAPI MsiEnumPatchesA(LPCSTR szProduct, DWORD iPatchIndex,
2157         LPSTR lpPatchBuf, LPSTR lpTransformsBuf, LPDWORD pcchTransformsBuf)
2158 {
2159     LPWSTR product, transforms;
2160     WCHAR patch[GUID_SIZE];
2161     DWORD len;
2162     UINT r;
2163 
2164     TRACE("(%s %d %p %p %p)\n", debugstr_a(szProduct), iPatchIndex,
2165           lpPatchBuf, lpTransformsBuf, pcchTransformsBuf);
2166 
2167     if (!szProduct || !lpPatchBuf || !lpTransformsBuf || !pcchTransformsBuf)
2168         return ERROR_INVALID_PARAMETER;
2169 
2170     product = strdupAtoW(szProduct);
2171     if (!product)
2172         return ERROR_OUTOFMEMORY;
2173 
2174     len = *pcchTransformsBuf;
2175     transforms = msi_alloc( len * sizeof(WCHAR) );
2176     if (!transforms)
2177     {
2178         r = ERROR_OUTOFMEMORY;
2179         goto done;
2180     }
2181 
2182     r = MsiEnumPatchesW(product, iPatchIndex, patch, transforms, &len);
2183     if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA)
2184         goto done;
2185 
2186     WideCharToMultiByte(CP_ACP, 0, patch, -1, lpPatchBuf,
2187                         GUID_SIZE, NULL, NULL);
2188 
2189     if (!WideCharToMultiByte(CP_ACP, 0, transforms, -1, lpTransformsBuf,
2190                              *pcchTransformsBuf, NULL, NULL))
2191         r = ERROR_MORE_DATA;
2192 
2193     if (r == ERROR_MORE_DATA)
2194     {
2195         lpTransformsBuf[*pcchTransformsBuf - 1] = '\0';
2196         *pcchTransformsBuf = len * 2;
2197     }
2198     else
2199         *pcchTransformsBuf = strlen( lpTransformsBuf );
2200 
2201 done:
2202     msi_free(transforms);
2203     msi_free(product);
2204 
2205     return r;
2206 }
2207 
2208 /***********************************************************************
2209  * MsiEnumPatchesW            [MSI.@]
2210  */
2211 UINT WINAPI MsiEnumPatchesW(LPCWSTR szProduct, DWORD iPatchIndex,
2212         LPWSTR lpPatchBuf, LPWSTR lpTransformsBuf, LPDWORD pcchTransformsBuf)
2213 {
2214     WCHAR *transforms = NULL, squashed_pc[SQUASHED_GUID_SIZE];
2215     HKEY prod;
2216     DWORD idx = 0;
2217     UINT r;
2218 
2219     TRACE("(%s %d %p %p %p)\n", debugstr_w(szProduct), iPatchIndex,
2220           lpPatchBuf, lpTransformsBuf, pcchTransformsBuf);
2221 
2222     if (!szProduct || !squash_guid( szProduct, squashed_pc ))
2223         return ERROR_INVALID_PARAMETER;
2224 
2225     if (!lpPatchBuf || !lpTransformsBuf || !pcchTransformsBuf)
2226         return ERROR_INVALID_PARAMETER;
2227 
2228     if (MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED,
2229                               &prod, FALSE) != ERROR_SUCCESS &&
2230         MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
2231                               &prod, FALSE) != ERROR_SUCCESS &&
2232         MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_MACHINE,
2233                               &prod, FALSE) != ERROR_SUCCESS)
2234         return ERROR_UNKNOWN_PRODUCT;
2235 
2236     RegCloseKey(prod);
2237 
2238     r = msi_enum_patches(szProduct, NULL, MSIINSTALLCONTEXT_ALL,
2239                          MSIPATCHSTATE_ALL, iPatchIndex, &idx, lpPatchBuf,
2240                          NULL, NULL, NULL, NULL, &transforms);
2241     if (r != ERROR_SUCCESS)
2242         goto done;
2243 
2244     lstrcpynW(lpTransformsBuf, transforms, *pcchTransformsBuf);
2245     if (*pcchTransformsBuf <= lstrlenW(transforms))
2246     {
2247         r = ERROR_MORE_DATA;
2248         *pcchTransformsBuf = lstrlenW(transforms);
2249     }
2250     else
2251         *pcchTransformsBuf = lstrlenW(transforms);
2252 
2253 done:
2254     msi_free(transforms);
2255     return r;
2256 }
2257 
2258 UINT WINAPI MsiEnumProductsExA( LPCSTR product, LPCSTR usersid, DWORD ctx, DWORD index,
2259                                 CHAR installed_product[GUID_SIZE],
2260                                 MSIINSTALLCONTEXT *installed_ctx, LPSTR sid, LPDWORD sid_len )
2261 {
2262     UINT r;
2263     WCHAR installed_productW[GUID_SIZE], *productW = NULL, *usersidW = NULL, *sidW = NULL;
2264 
2265     TRACE("%s, %s, %u, %u, %p, %p, %p, %p\n", debugstr_a(product), debugstr_a(usersid),
2266           ctx, index, installed_product, installed_ctx, sid, sid_len);
2267 
2268     if (sid && !sid_len) return ERROR_INVALID_PARAMETER;
2269     if (product && !(productW = strdupAtoW( product ))) return ERROR_OUTOFMEMORY;
2270     if (usersid && !(usersidW = strdupAtoW( usersid )))
2271     {
2272         msi_free( productW );
2273         return ERROR_OUTOFMEMORY;
2274     }
2275     if (sid && !(sidW = msi_alloc( *sid_len * sizeof(WCHAR) )))
2276     {
2277         msi_free( usersidW );
2278         msi_free( productW );
2279         return ERROR_OUTOFMEMORY;
2280     }
2281     r = MsiEnumProductsExW( productW, usersidW, ctx, index, installed_productW,
2282                             installed_ctx, sidW, sid_len );
2283     if (r == ERROR_SUCCESS)
2284     {
2285         if (installed_product) WideCharToMultiByte( CP_ACP, 0, installed_productW, GUID_SIZE,
2286                                                     installed_product, GUID_SIZE, NULL, NULL );
2287         if (sid) WideCharToMultiByte( CP_ACP, 0, sidW, *sid_len + 1, sid, *sid_len + 1, NULL, NULL );
2288     }
2289     msi_free( productW );
2290     msi_free( usersidW );
2291     msi_free( sidW );
2292     return r;
2293 }
2294 
2295 static UINT fetch_machine_product( const WCHAR *match, DWORD index, DWORD *idx,
2296                                    WCHAR installed_product[GUID_SIZE],
2297                                    MSIINSTALLCONTEXT *installed_ctx, WCHAR *sid, DWORD *sid_len )
2298 {
2299     static const WCHAR productsW[] =
2300         {'S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s','\\',
2301          'I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s',0};
2302     UINT r;
2303     WCHAR product[SQUASHED_GUID_SIZE];
2304     DWORD i = 0, len;
2305     REGSAM access = KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY;
2306     HKEY key;
2307 
2308     if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, productsW, 0, access, &key ))
2309         return ERROR_NO_MORE_ITEMS;
2310 
2311     len = sizeof(product)/sizeof(product[0]);
2312     while (!RegEnumKeyExW( key, i, product, &len, NULL, NULL, NULL, NULL ))
2313     {
2314         if (match && strcmpW( match, product ))
2315         {
2316             i++;
2317             len = sizeof(product)/sizeof(product[0]);
2318             continue;
2319         }
2320         if (*idx == index) goto found;
2321         (*idx)++;
2322         len = sizeof(product)/sizeof(product[0]);
2323         i++;
2324     }
2325     RegCloseKey( key );
2326     return ERROR_NO_MORE_ITEMS;
2327 
2328 found:
2329     if (sid_len && *sid_len < 1)
2330     {
2331         *sid_len = 1;
2332         r = ERROR_MORE_DATA;
2333     }
2334     else
2335     {
2336         if (installed_product) unsquash_guid( product, installed_product );
2337         if (installed_ctx) *installed_ctx = MSIINSTALLCONTEXT_MACHINE;
2338         if (sid)
2339         {
2340             sid[0] = 0;
2341             *sid_len = 0;
2342         }
2343         r = ERROR_SUCCESS;
2344     }
2345     RegCloseKey( key );
2346     return r;
2347 }
2348 
2349 static UINT fetch_user_product( const WCHAR *match, const WCHAR *usersid, DWORD ctx, DWORD index,
2350                                 DWORD *idx, WCHAR installed_product[GUID_SIZE],
2351                                 MSIINSTALLCONTEXT *installed_ctx, WCHAR *sid, DWORD *sid_len )
2352 {
2353     static const WCHAR managedW[] =
2354         {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
2355          'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s',
2356          'i','o','n','\\','I','n','s','t','a','l','l','e','r','\\','M','a','n','a','g','e','d',0};
2357     static const WCHAR managed_productsW[] =
2358         {'\\','I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s',0};
2359     static const WCHAR unmanaged_productsW[] =
2360         {'\\','S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
2361          'I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s',0};
2362     UINT r;
2363     const WCHAR *subkey;
2364     WCHAR path[MAX_PATH], product[SQUASHED_GUID_SIZE], user[128];
2365     DWORD i = 0, j = 0, len_product, len_user;
2366     REGSAM access = KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY;
2367     HKEY key_users, key_products;
2368 
2369     if (ctx == MSIINSTALLCONTEXT_USERMANAGED)
2370     {
2371         subkey = managed_productsW;
2372         if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, managedW, 0, access, &key_users ))
2373             return ERROR_NO_MORE_ITEMS;
2374     }
2375     else if (ctx == MSIINSTALLCONTEXT_USERUNMANAGED)
2376     {
2377         subkey = unmanaged_productsW;
2378         if (RegOpenKeyExW( HKEY_USERS, NULL, 0, access, &key_users ))
2379             return ERROR_NO_MORE_ITEMS;
2380     }
2381     else return ERROR_INVALID_PARAMETER;
2382 
2383     len_user = sizeof(user)/sizeof(user[0]);
2384     while (!RegEnumKeyExW( key_users, i, user, &len_user, NULL, NULL, NULL, NULL ))
2385     {
2386         if (strcmpW( usersid, user ) && strcmpW( usersid, szAllSid ))
2387         {
2388             i++;
2389             len_user = sizeof(user)/sizeof(user[0]);
2390             continue;
2391         }
2392         strcpyW( path, user );
2393         strcatW( path, subkey );
2394         if (RegOpenKeyExW( key_users, path, 0, access, &key_products ))
2395         {
2396             i++;
2397             len_user = sizeof(user)/sizeof(user[0]);
2398             continue;
2399         }
2400         len_product = sizeof(product)/sizeof(product[0]);
2401         while (!RegEnumKeyExW( key_products, j, product, &len_product, NULL, NULL, NULL, NULL ))
2402         {
2403             if (match && strcmpW( match, product ))
2404             {
2405                 j++;
2406                 len_product = sizeof(product)/sizeof(product[0]);
2407                 continue;
2408             }
2409             if (*idx == index) goto found;
2410             (*idx)++;
2411             len_product = sizeof(product)/sizeof(product[0]);
2412             j++;
2413         }
2414         RegCloseKey( key_products );
2415         len_user = sizeof(user)/sizeof(user[0]);
2416         i++;
2417     }
2418     RegCloseKey( key_users );
2419     return ERROR_NO_MORE_ITEMS;
2420 
2421 found:
2422     if (sid_len && *sid_len <= len_user)
2423     {
2424         *sid_len = len_user;
2425         r = ERROR_MORE_DATA;
2426     }
2427     else
2428     {
2429         if (installed_product) unsquash_guid( product, installed_product );
2430         if (installed_ctx) *installed_ctx = ctx;
2431         if (sid)
2432         {
2433             strcpyW( sid, user );
2434             *sid_len = len_user;
2435         }
2436         r = ERROR_SUCCESS;
2437     }
2438     RegCloseKey( key_products );
2439     RegCloseKey( key_users );
2440     return r;
2441 }
2442 
2443 static UINT enum_products( const WCHAR *product, const WCHAR *usersid, DWORD ctx, DWORD index,
2444                            DWORD *idx, WCHAR installed_product[GUID_SIZE],
2445                            MSIINSTALLCONTEXT *installed_ctx, WCHAR *sid, DWORD *sid_len )
2446 {
2447     UINT r = ERROR_NO_MORE_ITEMS;
2448     WCHAR *user = NULL;
2449 
2450     if (!usersid)
2451     {
2452         usersid = user = get_user_sid();
2453         if (!user) return ERROR_FUNCTION_FAILED;
2454     }
2455     if (ctx & MSIINSTALLCONTEXT_MACHINE)
2456     {
2457         r = fetch_machine_product( product, index, idx, installed_product, installed_ctx,
2458                                    sid, sid_len );
2459         if (r != ERROR_NO_MORE_ITEMS) goto done;
2460     }
2461     if (ctx & MSIINSTALLCONTEXT_USERUNMANAGED)
2462     {
2463         r = fetch_user_product( product, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, index,
2464                                 idx, installed_product, installed_ctx, sid, sid_len );
2465         if (r != ERROR_NO_MORE_ITEMS) goto done;
2466     }
2467     if (ctx & MSIINSTALLCONTEXT_USERMANAGED)
2468     {
2469         r = fetch_user_product( product, usersid, MSIINSTALLCONTEXT_USERMANAGED, index,
2470                                 idx, installed_product, installed_ctx, sid, sid_len );
2471         if (r != ERROR_NO_MORE_ITEMS) goto done;
2472     }
2473 
2474 done:
2475     LocalFree( user );
2476     return r;
2477 }
2478 
2479 UINT WINAPI MsiEnumProductsExW( LPCWSTR product, LPCWSTR usersid, DWORD ctx, DWORD index,
2480                                 WCHAR installed_product[GUID_SIZE],
2481                                 MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid, LPDWORD sid_len )
2482 {
2483     UINT r;
2484     DWORD idx = 0;
2485     static DWORD last_index;
2486 
2487     TRACE("%s, %s, %u, %u, %p, %p, %p, %p\n", debugstr_w(product), debugstr_w(usersid),
2488           ctx, index, installed_product, installed_ctx, sid, sid_len);
2489 
2490     if ((sid && !sid_len) || !ctx || (usersid && ctx == MSIINSTALLCONTEXT_MACHINE))
2491         return ERROR_INVALID_PARAMETER;
2492 
2493     if (index && index - last_index != 1)
2494         return ERROR_INVALID_PARAMETER;
2495 
2496     if (!index) last_index = 0;
2497 
2498     r = enum_products( product, usersid, ctx, index, &idx, installed_product, installed_ctx,
2499                        sid, sid_len );
2500     if (r == ERROR_SUCCESS)
2501         last_index = index;
2502     else
2503         last_index = 0;
2504 
2505     return r;
2506 }
2507