msi: Open the correct key and return INSTALLSTATE_ADVERTISED if it's missing.
[wine] / dlls / msi / registry.c
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 <stdarg.h>
23
24 #define COBJMACROS
25 #define NONAMELESSUNION
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winreg.h"
30 #include "winnls.h"
31 #include "shlwapi.h"
32 #include "wine/debug.h"
33 #include "msi.h"
34 #include "msipriv.h"
35 #include "wincrypt.h"
36 #include "wine/unicode.h"
37 #include "winver.h"
38 #include "winuser.h"
39 #include "sddl.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(msi);
42
43
44 /* 
45  * This module will be all the helper functions for registry access by the
46  * installer bits. 
47  */
48 static const WCHAR szUserFeatures_fmt[] = {
49 'S','o','f','t','w','a','r','e','\\',
50 'M','i','c','r','o','s','o','f','t','\\',
51 'I','n','s','t','a','l','l','e','r','\\',
52 'F','e','a','t','u','r','e','s','\\',
53 '%','s',0};
54
55 static const WCHAR szInstaller_Features[] = {
56 'S','o','f','t','w','a','r','e','\\',
57 'M','i','c','r','o','s','o','f','t','\\',
58 'W','i','n','d','o','w','s','\\',
59 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
60 'I','n','s','t','a','l','l','e','r','\\',
61 'F','e','a','t','u','r','e','s',0 };
62
63 static const WCHAR szUserDataFeatures_fmt[] = {
64 'S','o','f','t','w','a','r','e','\\',
65 'M','i','c','r','o','s','o','f','t','\\',
66 'W','i','n','d','o','w','s','\\',
67 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
68 'I','n','s','t','a','l','l','e','r','\\',
69 'U','s','e','r','D','a','t','a','\\',
70 '%','s','\\','P','r','o','d','u','c','t','s','\\',
71 '%','s','\\','F','e','a','t','u','r','e','s',0};
72
73 static const WCHAR szInstaller_Features_fmt[] = {
74 'S','o','f','t','w','a','r','e','\\',
75 'M','i','c','r','o','s','o','f','t','\\',
76 'W','i','n','d','o','w','s','\\',
77 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
78 'I','n','s','t','a','l','l','e','r','\\',
79 'F','e','a','t','u','r','e','s','\\',
80 '%','s',0};
81
82 static const WCHAR szInstaller_Components[] = {
83 'S','o','f','t','w','a','r','e','\\',
84 'M','i','c','r','o','s','o','f','t','\\',
85 'W','i','n','d','o','w','s','\\',
86 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
87 'I','n','s','t','a','l','l','e','r','\\',
88 'C','o','m','p','o','n','e','n','t','s',0 };
89
90 static const WCHAR szInstaller_Components_fmt[] = {
91 'S','o','f','t','w','a','r','e','\\',
92 'M','i','c','r','o','s','o','f','t','\\',
93 'W','i','n','d','o','w','s','\\',
94 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
95 'I','n','s','t','a','l','l','e','r','\\',
96 'C','o','m','p','o','n','e','n','t','s','\\',
97 '%','s',0};
98
99 static const WCHAR szUser_Components_fmt[] = {
100 'S','o','f','t','w','a','r','e','\\',
101 'M','i','c','r','o','s','o','f','t','\\',
102 'I','n','s','t','a','l','l','e','r','\\',
103 'C','o','m','p','o','n','e','n','t','s','\\',
104 '%','s',0};
105
106 static const WCHAR szUninstall_fmt[] = {
107 'S','o','f','t','w','a','r','e','\\',
108 'M','i','c','r','o','s','o','f','t','\\',
109 'W','i','n','d','o','w','s','\\',
110 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
111 'U','n','i','n','s','t','a','l','l','\\',
112 '%','s',0 };
113
114 static const WCHAR szUserProduct_fmt[] = {
115 'S','o','f','t','w','a','r','e','\\',
116 'M','i','c','r','o','s','o','f','t','\\',
117 'I','n','s','t','a','l','l','e','r','\\',
118 'P','r','o','d','u','c','t','s','\\',
119 '%','s',0};
120
121 static const WCHAR szUserPatch_fmt[] = {
122 'S','o','f','t','w','a','r','e','\\',
123 'M','i','c','r','o','s','o','f','t','\\',
124 'I','n','s','t','a','l','l','e','r','\\',
125 'P','a','t','c','h','e','s','\\',
126 '%','s',0};
127
128 static const WCHAR szInstaller_Products[] = {
129 'S','o','f','t','w','a','r','e','\\',
130 'M','i','c','r','o','s','o','f','t','\\',
131 'W','i','n','d','o','w','s','\\',
132 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
133 'I','n','s','t','a','l','l','e','r','\\',
134 'P','r','o','d','u','c','t','s',0};
135
136 static const WCHAR szInstaller_Products_fmt[] = {
137 'S','o','f','t','w','a','r','e','\\',
138 'M','i','c','r','o','s','o','f','t','\\',
139 'W','i','n','d','o','w','s','\\',
140 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
141 'I','n','s','t','a','l','l','e','r','\\',
142 'P','r','o','d','u','c','t','s','\\',
143 '%','s',0};
144
145 static const WCHAR szInstaller_Patches_fmt[] = {
146 'S','o','f','t','w','a','r','e','\\',
147 'M','i','c','r','o','s','o','f','t','\\',
148 'W','i','n','d','o','w','s','\\',
149 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
150 'I','n','s','t','a','l','l','e','r','\\',
151 'P','a','t','c','h','e','s','\\',
152 '%','s',0};
153
154 static const WCHAR szInstaller_UpgradeCodes_fmt[] = {
155 'S','o','f','t','w','a','r','e','\\',
156 'M','i','c','r','o','s','o','f','t','\\',
157 'W','i','n','d','o','w','s','\\',
158 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
159 'I','n','s','t','a','l','l','e','r','\\',
160 'U','p','g','r','a','d','e','C','o','d','e','s','\\',
161 '%','s',0};
162
163 static const WCHAR szInstaller_UserUpgradeCodes_fmt[] = {
164 'S','o','f','t','w','a','r','e','\\',
165 'M','i','c','r','o','s','o','f','t','\\',
166 'I','n','s','t','a','l','l','e','r','\\',
167 'U','p','g','r','a','d','e','C','o','d','e','s','\\',
168 '%','s',0};
169
170 static const WCHAR szUserDataProd_fmt[] = {
171 'S','o','f','t','w','a','r','e','\\',
172 'M','i','c','r','o','s','o','f','t','\\',
173 'W','i','n','d','o','w','s','\\',
174 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
175 'I','n','s','t','a','l','l','e','r','\\',
176 'U','s','e','r','D','a','t','a','\\',
177 '%','s','\\','P','r','o','d','u','c','t','s','\\','%','s',0};
178
179 static const WCHAR szInstallProperties_fmt[] = {
180 'S','o','f','t','w','a','r','e','\\',
181 'M','i','c','r','o','s','o','f','t','\\',
182 'W','i','n','d','o','w','s','\\',
183 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
184 'I','n','s','t','a','l','l','e','r','\\',
185 'U','s','e','r','D','a','t','a','\\',
186 '%','s','\\','P','r','o','d','u','c','t','s','\\','%','s','\\',
187 'I','n','s','t','a','l','l','P','r','o','p','e','r','t','i','e','s',0};
188
189
190 #define SQUISH_GUID_SIZE 33
191
192 BOOL unsquash_guid(LPCWSTR in, LPWSTR out)
193 {
194     DWORD i,n=0;
195
196     out[n++]='{';
197     for(i=0; i<8; i++)
198         out[n++] = in[7-i];
199     out[n++]='-';
200     for(i=0; i<4; i++)
201         out[n++] = in[11-i];
202     out[n++]='-';
203     for(i=0; i<4; i++)
204         out[n++] = in[15-i];
205     out[n++]='-';
206     for(i=0; i<2; i++)
207     {
208         out[n++] = in[17+i*2];
209         out[n++] = in[16+i*2];
210     }
211     out[n++]='-';
212     for( ; i<8; i++)
213     {
214         out[n++] = in[17+i*2];
215         out[n++] = in[16+i*2];
216     }
217     out[n++]='}';
218     out[n]=0;
219     return TRUE;
220 }
221
222 BOOL squash_guid(LPCWSTR in, LPWSTR out)
223 {
224     DWORD i,n=1;
225     GUID guid;
226
227     if (FAILED(CLSIDFromString((LPOLESTR)in, &guid)))
228         return FALSE;
229
230     for(i=0; i<8; i++)
231         out[7-i] = in[n++];
232     n++;
233     for(i=0; i<4; i++)
234         out[11-i] = in[n++];
235     n++;
236     for(i=0; i<4; i++)
237         out[15-i] = in[n++];
238     n++;
239     for(i=0; i<2; i++)
240     {
241         out[17+i*2] = in[n++];
242         out[16+i*2] = in[n++];
243     }
244     n++;
245     for( ; i<8; i++)
246     {
247         out[17+i*2] = in[n++];
248         out[16+i*2] = in[n++];
249     }
250     out[32]=0;
251     return TRUE;
252 }
253
254
255 /* tables for encoding and decoding base85 */
256 static const unsigned char table_dec85[0x80] = {
257 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
258 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
259 0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff,
260 0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17,
261 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
262 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36,
263 0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
264 0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff,
265 };
266
267 static const char table_enc85[] =
268 "!$%&'()*+,-.0123456789=?@ABCDEFGHIJKLMNO"
269 "PQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwx"
270 "yz{}~";
271
272 /*
273  *  Converts a base85 encoded guid into a GUID pointer
274  *  Base85 encoded GUIDs should be 20 characters long.
275  *
276  *  returns TRUE if successful, FALSE if not
277  */
278 BOOL decode_base85_guid( LPCWSTR str, GUID *guid )
279 {
280     DWORD i, val = 0, base = 1, *p;
281
282     if (!str)
283         return FALSE;
284
285     p = (DWORD*) guid;
286     for( i=0; i<20; i++ )
287     {
288         if( (i%5) == 0 )
289         {
290             val = 0;
291             base = 1;
292         }
293         val += table_dec85[str[i]] * base;
294         if( str[i] >= 0x80 )
295             return FALSE;
296         if( table_dec85[str[i]] == 0xff )
297             return FALSE;
298         if( (i%5) == 4 )
299             p[i/5] = val;
300         base *= 85;
301     }
302     return TRUE;
303 }
304
305 /*
306  *  Encodes a base85 guid given a GUID pointer
307  *  Caller should provide a 21 character buffer for the encoded string.
308  *
309  *  returns TRUE if successful, FALSE if not
310  */
311 BOOL encode_base85_guid( GUID *guid, LPWSTR str )
312 {
313     unsigned int x, *p, i;
314
315     p = (unsigned int*) guid;
316     for( i=0; i<4; i++ )
317     {
318         x = p[i];
319         *str++ = table_enc85[x%85];
320         x = x/85;
321         *str++ = table_enc85[x%85];
322         x = x/85;
323         *str++ = table_enc85[x%85];
324         x = x/85;
325         *str++ = table_enc85[x%85];
326         x = x/85;
327         *str++ = table_enc85[x%85];
328     }
329     *str = 0;
330
331     return TRUE;
332 }
333
334 DWORD msi_version_str_to_dword(LPCWSTR p)
335 {
336     DWORD major, minor = 0, build = 0, version = 0;
337
338     if (!p)
339         return version;
340
341     major = atoiW(p);
342
343     p = strchrW(p, '.');
344     if (p)
345     {
346         minor = atoiW(p+1);
347         p = strchrW(p+1, '.');
348         if (p)
349             build = atoiW(p+1);
350     }
351
352     return MAKELONG(build, MAKEWORD(minor, major));
353 }
354
355 LPWSTR msi_version_dword_to_str(DWORD version)
356 {
357     const WCHAR fmt[] = { '%','u','.','%','u','.','%','u',0 };
358     LPWSTR str = msi_alloc(20);
359     sprintfW(str, fmt,
360              (version&0xff000000)>>24,
361              (version&0x00ff0000)>>16,
362               version&0x0000ffff);
363     return str;
364 }
365
366 LONG msi_reg_set_val_str( HKEY hkey, LPCWSTR name, LPCWSTR value )
367 {
368     DWORD len = value ? (lstrlenW(value) + 1) * sizeof (WCHAR) : 0;
369     return RegSetValueExW( hkey, name, 0, REG_SZ, (const BYTE *)value, len );
370 }
371
372 LONG msi_reg_set_val_multi_str( HKEY hkey, LPCWSTR name, LPCWSTR value )
373 {
374     LPCWSTR p = value;
375     while (*p) p += lstrlenW(p) + 1;
376     return RegSetValueExW( hkey, name, 0, REG_MULTI_SZ,
377                            (const BYTE *)value, (p + 1 - value) * sizeof(WCHAR) );
378 }
379
380 LONG msi_reg_set_val_dword( HKEY hkey, LPCWSTR name, DWORD val )
381 {
382     return RegSetValueExW( hkey, name, 0, REG_DWORD, (LPBYTE)&val, sizeof (DWORD) );
383 }
384
385 LONG msi_reg_set_subkey_val( HKEY hkey, LPCWSTR path, LPCWSTR name, LPCWSTR val )
386 {
387     HKEY hsubkey = 0;
388     LONG r;
389
390     r = RegCreateKeyW( hkey, path, &hsubkey );
391     if (r != ERROR_SUCCESS)
392         return r;
393     r = msi_reg_set_val_str( hsubkey, name, val );
394     RegCloseKey( hsubkey );
395     return r;
396 }
397
398 LPWSTR msi_reg_get_val_str( HKEY hkey, LPCWSTR name )
399 {
400     DWORD len = 0;
401     LPWSTR val;
402     LONG r;
403
404     r = RegQueryValueExW(hkey, name, NULL, NULL, NULL, &len);
405     if (r != ERROR_SUCCESS)
406         return NULL;
407
408     len += sizeof (WCHAR);
409     val = msi_alloc( len );
410     if (!val)
411         return NULL;
412     val[0] = 0;
413     RegQueryValueExW(hkey, name, NULL, NULL, (LPBYTE) val, &len);
414     return val;
415 }
416
417 BOOL msi_reg_get_val_dword( HKEY hkey, LPCWSTR name, DWORD *val)
418 {
419     DWORD type, len = sizeof (DWORD);
420     LONG r = RegQueryValueExW(hkey, name, NULL, &type, (LPBYTE) val, &len);
421     return r == ERROR_SUCCESS && type == REG_DWORD;
422 }
423
424 static UINT get_user_sid(LPWSTR *usersid)
425 {
426     HANDLE token;
427     BYTE buf[1024];
428     DWORD size;
429     PTOKEN_USER user;
430
431     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
432         return ERROR_FUNCTION_FAILED;
433
434     size = sizeof(buf);
435     if (!GetTokenInformation(token, TokenUser, (void *)buf, size, &size))
436         return ERROR_FUNCTION_FAILED;
437
438     user = (PTOKEN_USER)buf;
439     if (!ConvertSidToStringSidW(user->User.Sid, usersid))
440         return ERROR_FUNCTION_FAILED;
441
442     return ERROR_SUCCESS;
443 }
444
445 UINT MSIREG_OpenUninstallKey(LPCWSTR szProduct, HKEY* key, BOOL create)
446 {
447     UINT rc;
448     WCHAR keypath[0x200];
449     TRACE("%s\n",debugstr_w(szProduct));
450
451     sprintfW(keypath,szUninstall_fmt,szProduct);
452
453     if (create)
454         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
455     else
456         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
457
458     return rc;
459 }
460
461 UINT MSIREG_OpenUserProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create)
462 {
463     UINT rc;
464     WCHAR squished_pc[GUID_SIZE];
465     WCHAR keypath[0x200];
466
467     TRACE("%s\n",debugstr_w(szProduct));
468     squash_guid(szProduct,squished_pc);
469     TRACE("squished (%s)\n", debugstr_w(squished_pc));
470
471     sprintfW(keypath,szUserProduct_fmt,squished_pc);
472
473     if (create)
474         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
475     else
476         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
477
478     return rc;
479 }
480
481 UINT MSIREG_DeleteUserProductKey(LPCWSTR szProduct)
482 {
483     WCHAR squished_pc[GUID_SIZE];
484     WCHAR keypath[0x200];
485
486     TRACE("%s\n",debugstr_w(szProduct));
487     squash_guid(szProduct,squished_pc);
488     TRACE("squished (%s)\n", debugstr_w(squished_pc));
489
490     sprintfW(keypath,szUserProduct_fmt,squished_pc);
491
492     return RegDeleteTreeW(HKEY_CURRENT_USER, keypath);
493 }
494
495 UINT MSIREG_OpenUserPatchesKey(LPCWSTR szPatch, HKEY* key, BOOL create)
496 {
497     UINT rc;
498     WCHAR squished_pc[GUID_SIZE];
499     WCHAR keypath[0x200];
500
501     TRACE("%s\n",debugstr_w(szPatch));
502     squash_guid(szPatch,squished_pc);
503     TRACE("squished (%s)\n", debugstr_w(squished_pc));
504
505     sprintfW(keypath,szUserPatch_fmt,squished_pc);
506
507     if (create)
508         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
509     else
510         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
511
512     return rc;
513 }
514
515 UINT MSIREG_OpenUserFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create)
516 {
517     UINT rc;
518     WCHAR squished_pc[GUID_SIZE];
519     WCHAR keypath[0x200];
520
521     TRACE("%s\n",debugstr_w(szProduct));
522     squash_guid(szProduct,squished_pc);
523     TRACE("squished (%s)\n", debugstr_w(squished_pc));
524
525     sprintfW(keypath,szUserFeatures_fmt,squished_pc);
526
527     if (create)
528         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
529     else
530         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
531
532     return rc;
533 }
534
535 UINT MSIREG_OpenFeatures(HKEY* key)
536 {
537     return RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller_Features,key);
538 }
539
540 UINT MSIREG_OpenFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create)
541 {
542     UINT rc;
543     WCHAR squished_pc[GUID_SIZE];
544     WCHAR keypath[0x200];
545
546     TRACE("%s\n",debugstr_w(szProduct));
547     squash_guid(szProduct,squished_pc);
548     TRACE("squished (%s)\n", debugstr_w(squished_pc));
549
550     sprintfW(keypath,szInstaller_Features_fmt,squished_pc);
551
552     if (create)
553         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
554     else
555         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
556
557     return rc;
558 }
559
560 UINT MSIREG_OpenUserDataFeaturesKey(LPCWSTR szProduct, HKEY *key, BOOL create)
561 {
562     UINT rc;
563     WCHAR squished_pc[GUID_SIZE];
564     WCHAR keypath[0x200];
565     LPWSTR usersid;
566
567     TRACE("%s\n", debugstr_w(szProduct));
568     squash_guid(szProduct, squished_pc);
569     TRACE("squished (%s)\n", debugstr_w(squished_pc));
570
571     rc = get_user_sid(&usersid);
572     if (rc != ERROR_SUCCESS || !usersid)
573     {
574         ERR("Failed to retrieve user SID: %d\n", rc);
575         return rc;
576     }
577
578     sprintfW(keypath, szUserDataFeatures_fmt, usersid, squished_pc);
579
580     if (create)
581         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
582     else
583         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
584
585     msi_free(usersid);
586     return rc;
587 }
588
589 UINT MSIREG_OpenComponents(HKEY* key)
590 {
591     return RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller_Components,key);
592 }
593
594 UINT MSIREG_OpenComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create)
595 {
596     UINT rc;
597     WCHAR squished_cc[GUID_SIZE];
598     WCHAR keypath[0x200];
599
600     TRACE("%s\n",debugstr_w(szComponent));
601     squash_guid(szComponent,squished_cc);
602     TRACE("squished (%s)\n", debugstr_w(squished_cc));
603
604     sprintfW(keypath,szInstaller_Components_fmt,squished_cc);
605
606     if (create)
607         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
608     else
609         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
610
611     return rc;
612 }
613
614 UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create)
615 {
616     UINT rc;
617     WCHAR squished_cc[GUID_SIZE];
618     WCHAR keypath[0x200];
619
620     TRACE("%s\n",debugstr_w(szComponent));
621     squash_guid(szComponent,squished_cc);
622     TRACE("squished (%s)\n", debugstr_w(squished_cc));
623
624     sprintfW(keypath,szUser_Components_fmt,squished_cc);
625
626     if (create)
627         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
628     else
629         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
630
631     return rc;
632 }
633
634 UINT MSIREG_OpenUserDataProductKey(LPCWSTR szProduct, HKEY *key, BOOL create)
635 {
636     UINT rc;
637     WCHAR squished_pc[GUID_SIZE];
638     WCHAR keypath[0x200];
639     LPWSTR usersid;
640
641     TRACE("%s\n", debugstr_w(szProduct));
642     squash_guid(szProduct, squished_pc);
643     TRACE("squished (%s)\n", debugstr_w(squished_pc));
644
645     rc = get_user_sid(&usersid);
646     if (rc != ERROR_SUCCESS || !usersid)
647     {
648         ERR("Failed to retrieve user SID: %d\n", rc);
649         return rc;
650     }
651
652     sprintfW(keypath, szUserDataProd_fmt, usersid, squished_pc);
653
654     if (create)
655         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
656     else
657         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
658
659     msi_free(usersid);
660     return rc;
661 }
662
663 UINT MSIREG_OpenInstallPropertiesKey(LPCWSTR szProduct, HKEY *key, BOOL create)
664 {
665     UINT rc;
666     WCHAR squished_pc[GUID_SIZE];
667     WCHAR keypath[0x200];
668     LPWSTR usersid;
669
670     TRACE("%s\n", debugstr_w(szProduct));
671     squash_guid(szProduct, squished_pc);
672     TRACE("squished (%s)\n", debugstr_w(squished_pc));
673
674     rc = get_user_sid(&usersid);
675     if (rc != ERROR_SUCCESS || !usersid)
676     {
677         ERR("Failed to retrieve user SID: %d\n", rc);
678         return rc;
679     }
680
681     sprintfW(keypath, szInstallProperties_fmt, usersid, squished_pc);
682
683     if (create)
684         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
685     else
686         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
687
688     msi_free(usersid);
689     return rc;
690 }
691
692 UINT MSIREG_DeleteUserDataProductKey(LPCWSTR szProduct)
693 {
694     UINT rc;
695     WCHAR squished_pc[GUID_SIZE];
696     WCHAR keypath[0x200];
697     LPWSTR usersid;
698
699     TRACE("%s\n", debugstr_w(szProduct));
700     squash_guid(szProduct, squished_pc);
701     TRACE("squished (%s)\n", debugstr_w(squished_pc));
702
703     rc = get_user_sid(&usersid);
704     if (rc != ERROR_SUCCESS || !usersid)
705     {
706         ERR("Failed to retrieve user SID: %d\n", rc);
707         return rc;
708     }
709
710     sprintfW(keypath, szUserDataProd_fmt, usersid, squished_pc);
711
712     msi_free(usersid);
713     return RegDeleteTreeW(HKEY_LOCAL_MACHINE, keypath);
714 }
715
716 UINT MSIREG_OpenProducts(HKEY* key)
717 {
718     return RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller_Products,key);
719 }
720
721 UINT MSIREG_OpenProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create)
722 {
723     UINT rc;
724     WCHAR squished_pc[GUID_SIZE];
725     WCHAR keypath[0x200];
726
727     TRACE("%s\n",debugstr_w(szProduct));
728     squash_guid(szProduct,squished_pc);
729     TRACE("squished (%s)\n", debugstr_w(squished_pc));
730
731     sprintfW(keypath,szInstaller_Products_fmt,squished_pc);
732
733     if (create)
734         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
735     else
736         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
737
738     return rc;
739 }
740
741 UINT MSIREG_DeleteProductKey(LPCWSTR szProduct)
742 {
743     WCHAR squished_pc[GUID_SIZE];
744     WCHAR keypath[0x200];
745
746     TRACE("%s\n", debugstr_w(szProduct));
747     squash_guid(szProduct, squished_pc);
748     TRACE("squished (%s)\n", debugstr_w(squished_pc));
749
750     sprintfW(keypath, szInstaller_Products_fmt, squished_pc);
751
752     return RegDeleteTreeW(HKEY_LOCAL_MACHINE, keypath);
753 }
754
755 UINT MSIREG_OpenPatchesKey(LPCWSTR szPatch, HKEY* key, BOOL create)
756 {
757     UINT rc;
758     WCHAR squished_pc[GUID_SIZE];
759     WCHAR keypath[0x200];
760
761     TRACE("%s\n",debugstr_w(szPatch));
762     squash_guid(szPatch,squished_pc);
763     TRACE("squished (%s)\n", debugstr_w(squished_pc));
764
765     sprintfW(keypath,szInstaller_Patches_fmt,squished_pc);
766
767     if (create)
768         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
769     else
770         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
771
772     return rc;
773 }
774
775 UINT MSIREG_OpenUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create)
776 {
777     UINT rc;
778     WCHAR squished_pc[GUID_SIZE];
779     WCHAR keypath[0x200];
780
781     TRACE("%s\n",debugstr_w(szUpgradeCode));
782     squash_guid(szUpgradeCode,squished_pc);
783     TRACE("squished (%s)\n", debugstr_w(squished_pc));
784
785     sprintfW(keypath,szInstaller_UpgradeCodes_fmt,squished_pc);
786
787     if (create)
788         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
789     else
790         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
791
792     return rc;
793 }
794
795 UINT MSIREG_OpenUserUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create)
796 {
797     UINT rc;
798     WCHAR squished_pc[GUID_SIZE];
799     WCHAR keypath[0x200];
800
801     TRACE("%s\n",debugstr_w(szUpgradeCode));
802     squash_guid(szUpgradeCode,squished_pc);
803     TRACE("squished (%s)\n", debugstr_w(squished_pc));
804
805     sprintfW(keypath,szInstaller_UserUpgradeCodes_fmt,squished_pc);
806
807     if (create)
808         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
809     else
810         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
811
812     return rc;
813 }
814
815
816 /*************************************************************************
817  *  MsiDecomposeDescriptorW   [MSI.@]
818  *
819  * Decomposes an MSI descriptor into product, feature and component parts.
820  * An MSI descriptor is a string of the form:
821  *   [base 85 guid] [feature code] '>' [base 85 guid]
822  *
823  * PARAMS
824  *   szDescriptor  [I]  the descriptor to decompose
825  *   szProduct     [O]  buffer of MAX_FEATURE_CHARS+1 for the product guid
826  *   szFeature     [O]  buffer of MAX_FEATURE_CHARS+1 for the feature code
827  *   szComponent   [O]  buffer of MAX_FEATURE_CHARS+1 for the component guid
828  *   pUsed         [O]  the length of the descriptor
829  *
830  * RETURNS
831  *   ERROR_SUCCESS             if everything worked correctly
832  *   ERROR_INVALID_PARAMETER   if the descriptor was invalid
833  *
834  */
835 UINT WINAPI MsiDecomposeDescriptorW( LPCWSTR szDescriptor, LPWSTR szProduct,
836                 LPWSTR szFeature, LPWSTR szComponent, DWORD *pUsed )
837 {
838     UINT r, len;
839     LPWSTR p;
840     GUID product, component;
841
842     TRACE("%s %p %p %p %p\n", debugstr_w(szDescriptor), szProduct,
843           szFeature, szComponent, pUsed);
844
845     r = decode_base85_guid( szDescriptor, &product );
846     if( !r )
847         return ERROR_INVALID_PARAMETER;
848
849     TRACE("product %s\n", debugstr_guid( &product ));
850
851     p = strchrW(&szDescriptor[20],'>');
852     if( !p )
853         return ERROR_INVALID_PARAMETER;
854
855     len = (p - &szDescriptor[20]);
856     if( len > MAX_FEATURE_CHARS )
857         return ERROR_INVALID_PARAMETER;
858
859     TRACE("feature %s\n", debugstr_wn( &szDescriptor[20], len ));
860
861     r = decode_base85_guid( p+1, &component );
862     if( !r )
863         return ERROR_INVALID_PARAMETER;
864
865     TRACE("component %s\n", debugstr_guid( &component ));
866
867     if (szProduct)
868         StringFromGUID2( &product, szProduct, MAX_FEATURE_CHARS+1 );
869     if (szComponent)
870         StringFromGUID2( &component, szComponent, MAX_FEATURE_CHARS+1 );
871     if (szFeature)
872     {
873         memcpy( szFeature, &szDescriptor[20], len*sizeof(WCHAR) );
874         szFeature[len] = 0;
875     }
876     len = ( &p[21] - szDescriptor );
877
878     TRACE("length = %d\n", len);
879     *pUsed = len;
880
881     return ERROR_SUCCESS;
882 }
883
884 UINT WINAPI MsiDecomposeDescriptorA( LPCSTR szDescriptor, LPSTR szProduct,
885                 LPSTR szFeature, LPSTR szComponent, DWORD *pUsed )
886 {
887     WCHAR product[MAX_FEATURE_CHARS+1];
888     WCHAR feature[MAX_FEATURE_CHARS+1];
889     WCHAR component[MAX_FEATURE_CHARS+1];
890     LPWSTR str = NULL, p = NULL, f = NULL, c = NULL;
891     UINT r;
892
893     TRACE("%s %p %p %p %p\n", debugstr_a(szDescriptor), szProduct,
894           szFeature, szComponent, pUsed);
895
896     str = strdupAtoW( szDescriptor );
897     if( szDescriptor && !str )
898         return ERROR_OUTOFMEMORY;
899
900     if (szProduct)
901         p = product;
902     if (szFeature)
903         f = feature;
904     if (szComponent)
905         c = component;
906
907     r = MsiDecomposeDescriptorW( str, p, f, c, pUsed );
908
909     if (r == ERROR_SUCCESS)
910     {
911         WideCharToMultiByte( CP_ACP, 0, p, -1,
912                              szProduct, MAX_FEATURE_CHARS+1, NULL, NULL );
913         WideCharToMultiByte( CP_ACP, 0, f, -1,
914                              szFeature, MAX_FEATURE_CHARS+1, NULL, NULL );
915         WideCharToMultiByte( CP_ACP, 0, c, -1,
916                              szComponent, MAX_FEATURE_CHARS+1, NULL, NULL );
917     }
918
919     msi_free( str );
920
921     return r;
922 }
923
924 UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid)
925 {
926     DWORD r;
927     WCHAR szwGuid[GUID_SIZE];
928
929     TRACE("%d %p\n", index, lpguid);
930
931     if (NULL == lpguid)
932         return ERROR_INVALID_PARAMETER;
933     r = MsiEnumProductsW(index, szwGuid);
934     if( r == ERROR_SUCCESS )
935         WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
936
937     return r;
938 }
939
940 UINT WINAPI MsiEnumProductsW(DWORD index, LPWSTR lpguid)
941 {
942     HKEY hkeyProducts = 0;
943     DWORD r;
944     WCHAR szKeyName[SQUISH_GUID_SIZE];
945
946     TRACE("%d %p\n", index, lpguid);
947
948     if (NULL == lpguid)
949         return ERROR_INVALID_PARAMETER;
950
951     r = MSIREG_OpenProducts(&hkeyProducts);
952     if( r != ERROR_SUCCESS )
953         return ERROR_NO_MORE_ITEMS;
954
955     r = RegEnumKeyW(hkeyProducts, index, szKeyName, SQUISH_GUID_SIZE);
956     if( r == ERROR_SUCCESS )
957         unsquash_guid(szKeyName, lpguid);
958     RegCloseKey(hkeyProducts);
959
960     return r;
961 }
962
963 UINT WINAPI MsiEnumFeaturesA(LPCSTR szProduct, DWORD index, 
964       LPSTR szFeature, LPSTR szParent)
965 {
966     DWORD r;
967     WCHAR szwFeature[GUID_SIZE], szwParent[GUID_SIZE];
968     LPWSTR szwProduct = NULL;
969
970     TRACE("%s %d %p %p\n", debugstr_a(szProduct), index, szFeature, szParent);
971
972     if( szProduct )
973     {
974         szwProduct = strdupAtoW( szProduct );
975         if( !szwProduct )
976             return ERROR_OUTOFMEMORY;
977     }
978
979     r = MsiEnumFeaturesW(szwProduct, index, szwFeature, szwParent);
980     if( r == ERROR_SUCCESS )
981     {
982         WideCharToMultiByte(CP_ACP, 0, szwFeature, -1,
983                             szFeature, GUID_SIZE, NULL, NULL);
984         WideCharToMultiByte(CP_ACP, 0, szwParent, -1,
985                             szParent, GUID_SIZE, NULL, NULL);
986     }
987
988     msi_free( szwProduct);
989
990     return r;
991 }
992
993 UINT WINAPI MsiEnumFeaturesW(LPCWSTR szProduct, DWORD index, 
994       LPWSTR szFeature, LPWSTR szParent)
995 {
996     HKEY hkeyProduct = 0;
997     DWORD r, sz;
998
999     TRACE("%s %d %p %p\n", debugstr_w(szProduct), index, szFeature, szParent);
1000
1001     if( !szProduct )
1002         return ERROR_INVALID_PARAMETER;
1003
1004     r = MSIREG_OpenFeaturesKey(szProduct,&hkeyProduct,FALSE);
1005     if( r != ERROR_SUCCESS )
1006         return ERROR_NO_MORE_ITEMS;
1007
1008     sz = GUID_SIZE;
1009     r = RegEnumValueW(hkeyProduct, index, szFeature, &sz, NULL, NULL, NULL, NULL);
1010     RegCloseKey(hkeyProduct);
1011
1012     return r;
1013 }
1014
1015 UINT WINAPI MsiEnumComponentsA(DWORD index, LPSTR lpguid)
1016 {
1017     DWORD r;
1018     WCHAR szwGuid[GUID_SIZE];
1019
1020     TRACE("%d %p\n", index, lpguid);
1021
1022     r = MsiEnumComponentsW(index, szwGuid);
1023     if( r == ERROR_SUCCESS )
1024         WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
1025
1026     return r;
1027 }
1028
1029 UINT WINAPI MsiEnumComponentsW(DWORD index, LPWSTR lpguid)
1030 {
1031     HKEY hkeyComponents = 0;
1032     DWORD r;
1033     WCHAR szKeyName[SQUISH_GUID_SIZE];
1034
1035     TRACE("%d %p\n", index, lpguid);
1036
1037     r = MSIREG_OpenComponents(&hkeyComponents);
1038     if( r != ERROR_SUCCESS )
1039         return ERROR_NO_MORE_ITEMS;
1040
1041     r = RegEnumKeyW(hkeyComponents, index, szKeyName, SQUISH_GUID_SIZE);
1042     if( r == ERROR_SUCCESS )
1043         unsquash_guid(szKeyName, lpguid);
1044     RegCloseKey(hkeyComponents);
1045
1046     return r;
1047 }
1048
1049 UINT WINAPI MsiEnumClientsA(LPCSTR szComponent, DWORD index, LPSTR szProduct)
1050 {
1051     DWORD r;
1052     WCHAR szwProduct[GUID_SIZE];
1053     LPWSTR szwComponent = NULL;
1054
1055     TRACE("%s %d %p\n", debugstr_a(szComponent), index, szProduct);
1056
1057     if( szComponent )
1058     {
1059         szwComponent = strdupAtoW( szComponent );
1060         if( !szwComponent )
1061             return ERROR_OUTOFMEMORY;
1062     }
1063
1064     r = MsiEnumClientsW(szComponent?szwComponent:NULL, index, szwProduct);
1065     if( r == ERROR_SUCCESS )
1066     {
1067         WideCharToMultiByte(CP_ACP, 0, szwProduct, -1,
1068                             szProduct, GUID_SIZE, NULL, NULL);
1069     }
1070
1071     msi_free( szwComponent);
1072
1073     return r;
1074 }
1075
1076 UINT WINAPI MsiEnumClientsW(LPCWSTR szComponent, DWORD index, LPWSTR szProduct)
1077 {
1078     HKEY hkeyComp = 0;
1079     DWORD r, sz;
1080     WCHAR szValName[SQUISH_GUID_SIZE];
1081
1082     TRACE("%s %d %p\n", debugstr_w(szComponent), index, szProduct);
1083
1084     r = MSIREG_OpenComponentsKey(szComponent,&hkeyComp,FALSE);
1085     if( r != ERROR_SUCCESS )
1086         return ERROR_NO_MORE_ITEMS;
1087
1088     sz = SQUISH_GUID_SIZE;
1089     r = RegEnumValueW(hkeyComp, index, szValName, &sz, NULL, NULL, NULL, NULL);
1090     if( r == ERROR_SUCCESS )
1091         unsquash_guid(szValName, szProduct);
1092
1093     RegCloseKey(hkeyComp);
1094
1095     return r;
1096 }
1097
1098 static UINT WINAPI MSI_EnumComponentQualifiers( LPCWSTR szComponent, DWORD iIndex,
1099                 awstring *lpQualBuf, DWORD* pcchQual,
1100                 awstring *lpAppBuf, DWORD* pcchAppBuf )
1101 {
1102     DWORD name_sz, val_sz, name_max, val_max, type, ofs;
1103     LPWSTR name = NULL, val = NULL;
1104     UINT r, r2;
1105     HKEY key;
1106
1107     TRACE("%s %08x %p %p %p %p\n", debugstr_w(szComponent), iIndex,
1108           lpQualBuf, pcchQual, lpAppBuf, pcchAppBuf);
1109
1110     if (!szComponent)
1111         return ERROR_INVALID_PARAMETER;
1112
1113     r = MSIREG_OpenUserComponentsKey( szComponent, &key, FALSE );
1114     if (r != ERROR_SUCCESS)
1115         return ERROR_UNKNOWN_COMPONENT;
1116
1117     /* figure out how big the name is we want to return */
1118     name_max = 0x10;
1119     r = ERROR_OUTOFMEMORY;
1120     name = msi_alloc( name_max * sizeof(WCHAR) );
1121     if (!name)
1122         goto end;
1123
1124     val_max = 0x10;
1125     r = ERROR_OUTOFMEMORY;
1126     val = msi_alloc( val_max );
1127     if (!val)
1128         goto end;
1129
1130     /* loop until we allocate enough memory */
1131     while (1)
1132     {
1133         name_sz = name_max;
1134         val_sz = val_max;
1135         r = RegEnumValueW( key, iIndex, name, &name_sz,
1136                            NULL, &type, (LPBYTE)val, &val_sz );
1137         if (r == ERROR_SUCCESS)
1138             break;
1139         if (r != ERROR_MORE_DATA)
1140             goto end;
1141  
1142         if (type != REG_MULTI_SZ)
1143         {
1144             ERR("component data has wrong type (%d)\n", type);
1145             goto end;
1146         }
1147
1148         r = ERROR_OUTOFMEMORY;
1149         if ((name_sz+1) >= name_max)
1150         {
1151             name_max *= 2;
1152             msi_free( name );
1153             name = msi_alloc( name_max * sizeof (WCHAR) );
1154             if (!name)
1155                 goto end;
1156             continue;
1157         }
1158         if (val_sz > val_max)
1159         {
1160             val_max = val_sz + sizeof (WCHAR);
1161             msi_free( val );
1162             val = msi_alloc( val_max * sizeof (WCHAR) );
1163             if (!val)
1164                 goto end;
1165             continue;
1166         }
1167         ERR("should be enough data, but isn't %d %d\n", name_sz, val_sz );
1168         goto end;
1169     }
1170
1171     ofs = 0;
1172     r = MsiDecomposeDescriptorW( val, NULL, NULL, NULL, &ofs );
1173     if (r != ERROR_SUCCESS)
1174         goto end;
1175
1176     TRACE("Providing %s and %s\n", debugstr_w(name), debugstr_w(val+ofs));
1177
1178     r = msi_strcpy_to_awstring( name, lpQualBuf, pcchQual );
1179     r2 = msi_strcpy_to_awstring( val+ofs, lpAppBuf, pcchAppBuf );
1180
1181     if (r2 != ERROR_SUCCESS)
1182         r = r2;
1183
1184 end:
1185     msi_free(val);
1186     msi_free(name);
1187     RegCloseKey(key);
1188
1189     return r;
1190 }
1191
1192 /*************************************************************************
1193  *  MsiEnumComponentQualifiersA [MSI.@]
1194  */
1195 UINT WINAPI MsiEnumComponentQualifiersA( LPCSTR szComponent, DWORD iIndex,
1196                 LPSTR lpQualifierBuf, DWORD* pcchQualifierBuf,
1197                 LPSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf )
1198 {
1199     awstring qual, appdata;
1200     LPWSTR comp;
1201     UINT r;
1202
1203     TRACE("%s %08x %p %p %p %p\n", debugstr_a(szComponent), iIndex,
1204           lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf,
1205           pcchApplicationDataBuf);
1206
1207     comp = strdupAtoW( szComponent );
1208     if (szComponent && !comp)
1209         return ERROR_OUTOFMEMORY;
1210
1211     qual.unicode = FALSE;
1212     qual.str.a = lpQualifierBuf;
1213
1214     appdata.unicode = FALSE;
1215     appdata.str.a = lpApplicationDataBuf;
1216
1217     r = MSI_EnumComponentQualifiers( comp, iIndex,
1218               &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf );
1219     msi_free( comp );
1220     return r;
1221 }
1222
1223 /*************************************************************************
1224  *  MsiEnumComponentQualifiersW [MSI.@]
1225  */
1226 UINT WINAPI MsiEnumComponentQualifiersW( LPCWSTR szComponent, DWORD iIndex,
1227                 LPWSTR lpQualifierBuf, DWORD* pcchQualifierBuf,
1228                 LPWSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf )
1229 {
1230     awstring qual, appdata;
1231
1232     TRACE("%s %08x %p %p %p %p\n", debugstr_w(szComponent), iIndex,
1233           lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf,
1234           pcchApplicationDataBuf);
1235
1236     qual.unicode = TRUE;
1237     qual.str.w = lpQualifierBuf;
1238
1239     appdata.unicode = TRUE;
1240     appdata.str.w = lpApplicationDataBuf;
1241
1242     return MSI_EnumComponentQualifiers( szComponent, iIndex,
1243                  &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf );
1244 }
1245
1246 /*************************************************************************
1247  *  MsiEnumRelatedProductsW   [MSI.@]
1248  *
1249  */
1250 UINT WINAPI MsiEnumRelatedProductsW(LPCWSTR szUpgradeCode, DWORD dwReserved,
1251                                     DWORD iProductIndex, LPWSTR lpProductBuf)
1252 {
1253     UINT r;
1254     HKEY hkey;
1255     DWORD dwSize = SQUISH_GUID_SIZE;
1256     WCHAR szKeyName[SQUISH_GUID_SIZE];
1257
1258     TRACE("%s %u %u %p\n", debugstr_w(szUpgradeCode), dwReserved,
1259           iProductIndex, lpProductBuf);
1260
1261     if (NULL == szUpgradeCode)
1262         return ERROR_INVALID_PARAMETER;
1263     if (NULL == lpProductBuf)
1264         return ERROR_INVALID_PARAMETER;
1265
1266     r = MSIREG_OpenUpgradeCodesKey(szUpgradeCode, &hkey, FALSE);
1267     if (r != ERROR_SUCCESS)
1268         return ERROR_NO_MORE_ITEMS;
1269
1270     r = RegEnumValueW(hkey, iProductIndex, szKeyName, &dwSize, NULL, NULL, NULL, NULL);
1271     if( r == ERROR_SUCCESS )
1272         unsquash_guid(szKeyName, lpProductBuf);
1273     RegCloseKey(hkey);
1274
1275     return r;
1276 }
1277
1278 /*************************************************************************
1279  *  MsiEnumRelatedProductsA   [MSI.@]
1280  *
1281  */
1282 UINT WINAPI MsiEnumRelatedProductsA(LPCSTR szUpgradeCode, DWORD dwReserved,
1283                                     DWORD iProductIndex, LPSTR lpProductBuf)
1284 {
1285     LPWSTR szwUpgradeCode = NULL;
1286     WCHAR productW[GUID_SIZE];
1287     UINT r;
1288
1289     TRACE("%s %u %u %p\n", debugstr_a(szUpgradeCode), dwReserved,
1290           iProductIndex, lpProductBuf);
1291
1292     if (szUpgradeCode)
1293     {
1294         szwUpgradeCode = strdupAtoW( szUpgradeCode );
1295         if( !szwUpgradeCode )
1296             return ERROR_OUTOFMEMORY;
1297     }
1298
1299     r = MsiEnumRelatedProductsW( szwUpgradeCode, dwReserved,
1300                                  iProductIndex, productW );
1301     if (r == ERROR_SUCCESS)
1302     {
1303         WideCharToMultiByte( CP_ACP, 0, productW, GUID_SIZE,
1304                              lpProductBuf, GUID_SIZE, NULL, NULL );
1305     }
1306     msi_free( szwUpgradeCode);
1307     return r;
1308 }
1309
1310 /***********************************************************************
1311  * MsiEnumPatchesA            [MSI.@]
1312  */
1313 UINT WINAPI MsiEnumPatchesA( LPCSTR szProduct, DWORD iPatchIndex,
1314         LPSTR lpPatchBuf, LPSTR lpTransformsBuf, DWORD* pcchTransformsBuf)
1315 {
1316     FIXME("%s %d %p %p %p\n", debugstr_a(szProduct),
1317           iPatchIndex, lpPatchBuf, lpTransformsBuf, pcchTransformsBuf);
1318     return ERROR_NO_MORE_ITEMS;
1319 }
1320
1321 /***********************************************************************
1322  * MsiEnumPatchesW            [MSI.@]
1323  */
1324 UINT WINAPI MsiEnumPatchesW( LPCWSTR szProduct, DWORD iPatchIndex,
1325         LPWSTR lpPatchBuf, LPWSTR lpTransformsBuf, DWORD* pcchTransformsBuf)
1326 {
1327     FIXME("%s %d %p %p %p\n", debugstr_w(szProduct),
1328           iPatchIndex, lpPatchBuf, lpTransformsBuf, pcchTransformsBuf);
1329     return ERROR_NO_MORE_ITEMS;
1330 }
1331
1332 UINT WINAPI MsiEnumProductsExA( LPCSTR szProductCode, LPCSTR szUserSid,
1333         DWORD dwContext, DWORD dwIndex, LPSTR szInstalledProductCode,
1334         MSIINSTALLCONTEXT* pdwInstalledContext, LPSTR szSid, LPDWORD pcchSid)
1335 {
1336     FIXME("%s %s %d %d %p %p %p %p\n", debugstr_a(szProductCode), debugstr_a(szUserSid),
1337           dwContext, dwIndex, szInstalledProductCode, pdwInstalledContext,
1338           szSid, pcchSid);
1339     return ERROR_NO_MORE_ITEMS;
1340 }
1341
1342 UINT WINAPI MsiEnumProductsExW( LPCWSTR szProductCode, LPCWSTR szUserSid,
1343         DWORD dwContext, DWORD dwIndex, LPWSTR szInstalledProductCode,
1344         MSIINSTALLCONTEXT* pdwInstalledContext, LPWSTR szSid, LPDWORD pcchSid)
1345 {
1346     FIXME("%s %s %d %d %p %p %p %p\n", debugstr_w(szProductCode), debugstr_w(szUserSid),
1347           dwContext, dwIndex, szInstalledProductCode, pdwInstalledContext,
1348           szSid, pcchSid);
1349     return ERROR_NO_MORE_ITEMS;
1350 }