Correct query quoting based on Mike's patch.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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
40 WINE_DEFAULT_DEBUG_CHANNEL(msi);
41
42
43 /* 
44  * This module will be all the helper functions for registry access by the
45  * installer bits. 
46  */
47 static const WCHAR szUserFeatures_fmt[] = {
48 'S','o','f','t','w','a','r','e','\\',
49 'M','i','c','r','o','s','o','f','t','\\',
50 'I','n','s','t','a','l','l','e','r','\\',
51 'F','e','a','t','u','r','e','s','\\',
52 '%','s',0};
53
54 static const WCHAR szInstaller_Features[] = {
55 'S','o','f','t','w','a','r','e','\\',
56 'M','i','c','r','o','s','o','f','t','\\',
57 'W','i','n','d','o','w','s','\\',
58 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
59 'I','n','s','t','a','l','l','e','r','\\',
60 'F','e','a','t','u','r','e','s',0 };
61
62 static const WCHAR szInstaller_Features_fmt[] = {
63 'S','o','f','t','w','a','r','e','\\',
64 'M','i','c','r','o','s','o','f','t','\\',
65 'W','i','n','d','o','w','s','\\',
66 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
67 'I','n','s','t','a','l','l','e','r','\\',
68 'F','e','a','t','u','r','e','s','\\',
69 '%','s',0};
70
71 static const WCHAR szInstaller_Components[] = {
72 'S','o','f','t','w','a','r','e','\\',
73 'M','i','c','r','o','s','o','f','t','\\',
74 'W','i','n','d','o','w','s','\\',
75 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
76 'I','n','s','t','a','l','l','e','r','\\',
77 'C','o','m','p','o','n','e','n','t','s',0 };
78
79 static const WCHAR szInstaller_Components_fmt[] = {
80 'S','o','f','t','w','a','r','e','\\',
81 'M','i','c','r','o','s','o','f','t','\\',
82 'W','i','n','d','o','w','s','\\',
83 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
84 'I','n','s','t','a','l','l','e','r','\\',
85 'C','o','m','p','o','n','e','n','t','s','\\',
86 '%','s',0};
87
88 static const WCHAR szUser_Components_fmt[] = {
89 'S','o','f','t','w','a','r','e','\\',
90 'M','i','c','r','o','s','o','f','t','\\',
91 'I','n','s','t','a','l','l','e','r','\\',
92 'C','o','m','p','o','n','e','n','t','s','\\',
93 '%','s',0};
94
95 static const WCHAR szUninstall_fmt[] = {
96 'S','o','f','t','w','a','r','e','\\',
97 'M','i','c','r','o','s','o','f','t','\\',
98 'W','i','n','d','o','w','s','\\',
99 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
100 'U','n','i','n','s','t','a','l','l','\\',
101 '%','s',0 };
102
103 static const WCHAR szUserProduct_fmt[] = {
104 'S','o','f','t','w','a','r','e','\\',
105 'M','i','c','r','o','s','o','f','t','\\',
106 'I','n','s','t','a','l','l','e','r','\\',
107 'P','r','o','d','u','c','t','s','\\',
108 '%','s',0};
109
110 static const WCHAR szInstaller_Products[] = {
111 'S','o','f','t','w','a','r','e','\\',
112 'M','i','c','r','o','s','o','f','t','\\',
113 'W','i','n','d','o','w','s','\\',
114 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
115 'I','n','s','t','a','l','l','e','r','\\',
116 'P','r','o','d','u','c','t','s',0};
117
118 static const WCHAR szInstaller_Products_fmt[] = {
119 'S','o','f','t','w','a','r','e','\\',
120 'M','i','c','r','o','s','o','f','t','\\',
121 'W','i','n','d','o','w','s','\\',
122 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
123 'I','n','s','t','a','l','l','e','r','\\',
124 'P','r','o','d','u','c','t','s','\\',
125 '%','s',0};
126
127 static const WCHAR szInstaller_UpgradeCodes[] = {
128 'S','o','f','t','w','a','r','e','\\',
129 'M','i','c','r','o','s','o','f','t','\\',
130 'W','i','n','d','o','w','s','\\',
131 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
132 'I','n','s','t','a','l','l','e','r','\\',
133 'U','p','g','r','a','d','e','C','o','d','e','s',0};
134
135 static const WCHAR szInstaller_UpgradeCodes_fmt[] = {
136 'S','o','f','t','w','a','r','e','\\',
137 'M','i','c','r','o','s','o','f','t','\\',
138 'W','i','n','d','o','w','s','\\',
139 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
140 'I','n','s','t','a','l','l','e','r','\\',
141 'U','p','g','r','a','d','e','C','o','d','e','s','\\',
142 '%','s',0};
143
144 #define SQUISH_GUID_SIZE 33
145
146 BOOL unsquash_guid(LPCWSTR in, LPWSTR out)
147 {
148     DWORD i,n=0;
149
150     out[n++]='{';
151     for(i=0; i<8; i++)
152         out[n++] = in[7-i];
153     out[n++]='-';
154     for(i=0; i<4; i++)
155         out[n++] = in[11-i];
156     out[n++]='-';
157     for(i=0; i<4; i++)
158         out[n++] = in[15-i];
159     out[n++]='-';
160     for(i=0; i<2; i++)
161     {
162         out[n++] = in[17+i*2];
163         out[n++] = in[16+i*2];
164     }
165     out[n++]='-';
166     for( ; i<8; i++)
167     {
168         out[n++] = in[17+i*2];
169         out[n++] = in[16+i*2];
170     }
171     out[n++]='}';
172     out[n]=0;
173     return TRUE;
174 }
175
176 BOOL squash_guid(LPCWSTR in, LPWSTR out)
177 {
178     DWORD i,n=0;
179
180     if(in[n++] != '{')
181         return FALSE;
182     for(i=0; i<8; i++)
183         out[7-i] = in[n++];
184     if(in[n++] != '-')
185         return FALSE;
186     for(i=0; i<4; i++)
187         out[11-i] = in[n++];
188     if(in[n++] != '-')
189         return FALSE;
190     for(i=0; i<4; i++)
191         out[15-i] = in[n++];
192     if(in[n++] != '-')
193         return FALSE;
194     for(i=0; i<2; i++)
195     {
196         out[17+i*2] = in[n++];
197         out[16+i*2] = in[n++];
198     }
199     if(in[n++] != '-')
200         return FALSE;
201     for( ; i<8; i++)
202     {
203         out[17+i*2] = in[n++];
204         out[16+i*2] = in[n++];
205     }
206     out[32]=0;
207     if(in[n++] != '}')
208         return FALSE;
209     if(in[n])
210         return FALSE;
211     return TRUE;
212 }
213
214
215 /* tables for encoding and decoding base85 */
216 static const unsigned char table_dec85[0x80] = {
217 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
218 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
219 0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff,
220 0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17,
221 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
222 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36,
223 0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
224 0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff,
225 };
226
227 static const char table_enc85[] =
228 "!$%&'()*+,-.0123456789=?@ABCDEFGHIJKLMNO"
229 "PQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwx"
230 "yz{}~";
231
232 /*
233  *  Converts a base85 encoded guid into a GUID pointer
234  *  Base85 encoded GUIDs should be 20 characters long.
235  *
236  *  returns TRUE if successful, FALSE if not
237  */
238 BOOL decode_base85_guid( LPCWSTR str, GUID *guid )
239 {
240     DWORD i, val = 0, base = 1, *p;
241
242     p = (DWORD*) guid;
243     for( i=0; i<20; i++ )
244     {
245         if( (i%5) == 0 )
246         {
247             val = 0;
248             base = 1;
249         }
250         val += table_dec85[str[i]] * base;
251         if( str[i] >= 0x80 )
252             return FALSE;
253         if( table_dec85[str[i]] == 0xff )
254             return FALSE;
255         if( (i%5) == 4 )
256             p[i/5] = val;
257         base *= 85;
258     }
259     return TRUE;
260 }
261
262 /*
263  *  Encodes a base85 guid given a GUID pointer
264  *  Caller should provide a 21 character buffer for the encoded string.
265  *
266  *  returns TRUE if successful, FALSE if not
267  */
268 BOOL encode_base85_guid( GUID *guid, LPWSTR str )
269 {
270     unsigned int x, *p, i;
271
272     p = (unsigned int*) guid;
273     for( i=0; i<4; i++ )
274     {
275         x = p[i];
276         *str++ = table_enc85[x%85];
277         x = x/85;
278         *str++ = table_enc85[x%85];
279         x = x/85;
280         *str++ = table_enc85[x%85];
281         x = x/85;
282         *str++ = table_enc85[x%85];
283         x = x/85;
284         *str++ = table_enc85[x%85];
285     }
286     *str = 0;
287
288     return TRUE;
289 }
290
291
292 UINT MSIREG_OpenUninstallKey(LPCWSTR szProduct, HKEY* key, BOOL create)
293 {
294     UINT rc;
295     WCHAR keypath[0x200];
296     TRACE("%s\n",debugstr_w(szProduct));
297
298     sprintfW(keypath,szUninstall_fmt,szProduct);
299
300     if (create)
301         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
302     else
303         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
304
305     return rc;
306 }
307
308 UINT MSIREG_OpenUserProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create)
309 {
310     UINT rc;
311     WCHAR squished_pc[GUID_SIZE];
312     WCHAR keypath[0x200];
313
314     TRACE("%s\n",debugstr_w(szProduct));
315     squash_guid(szProduct,squished_pc);
316     TRACE("squished (%s)\n", debugstr_w(squished_pc));
317
318     sprintfW(keypath,szUserProduct_fmt,squished_pc);
319
320     if (create)
321         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
322     else
323         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
324
325     return rc;
326 }
327
328 UINT MSIREG_OpenUserFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create)
329 {
330     UINT rc;
331     WCHAR squished_pc[GUID_SIZE];
332     WCHAR keypath[0x200];
333
334     TRACE("%s\n",debugstr_w(szProduct));
335     squash_guid(szProduct,squished_pc);
336     TRACE("squished (%s)\n", debugstr_w(squished_pc));
337
338     sprintfW(keypath,szUserFeatures_fmt,squished_pc);
339
340     if (create)
341         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
342     else
343         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
344
345     return rc;
346 }
347
348 UINT MSIREG_OpenFeatures(HKEY* key)
349 {
350     return RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller_Features,key);
351 }
352
353 UINT MSIREG_OpenFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create)
354 {
355     UINT rc;
356     WCHAR squished_pc[GUID_SIZE];
357     WCHAR keypath[0x200];
358
359     TRACE("%s\n",debugstr_w(szProduct));
360     squash_guid(szProduct,squished_pc);
361     TRACE("squished (%s)\n", debugstr_w(squished_pc));
362
363     sprintfW(keypath,szInstaller_Features_fmt,squished_pc);
364
365     if (create)
366         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
367     else
368         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
369
370     return rc;
371 }
372
373 UINT MSIREG_OpenComponents(HKEY* key)
374 {
375     return RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller_Components,key);
376 }
377
378 UINT MSIREG_OpenComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create)
379 {
380     UINT rc;
381     WCHAR squished_cc[GUID_SIZE];
382     WCHAR keypath[0x200];
383
384     TRACE("%s\n",debugstr_w(szComponent));
385     squash_guid(szComponent,squished_cc);
386     TRACE("squished (%s)\n", debugstr_w(squished_cc));
387
388     sprintfW(keypath,szInstaller_Components_fmt,squished_cc);
389
390     if (create)
391         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
392     else
393         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
394
395     return rc;
396 }
397
398 UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create)
399 {
400     UINT rc;
401     WCHAR squished_cc[GUID_SIZE];
402     WCHAR keypath[0x200];
403
404     TRACE("%s\n",debugstr_w(szComponent));
405     squash_guid(szComponent,squished_cc);
406     TRACE("squished (%s)\n", debugstr_w(squished_cc));
407
408     sprintfW(keypath,szUser_Components_fmt,squished_cc);
409
410     if (create)
411         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
412     else
413         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
414
415     return rc;
416 }
417
418 UINT MSIREG_OpenProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create)
419 {
420     UINT rc;
421     WCHAR squished_pc[GUID_SIZE];
422     WCHAR keypath[0x200];
423
424     TRACE("%s\n",debugstr_w(szProduct));
425     squash_guid(szProduct,squished_pc);
426     TRACE("squished (%s)\n", debugstr_w(squished_pc));
427
428     sprintfW(keypath,szInstaller_Products_fmt,squished_pc);
429
430     if (create)
431         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
432     else
433         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
434
435     return rc;
436 }
437
438 UINT MSIREG_OpenUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create)
439 {
440     UINT rc;
441     WCHAR squished_pc[GUID_SIZE];
442     WCHAR keypath[0x200];
443
444     TRACE("%s\n",debugstr_w(szUpgradeCode));
445     squash_guid(szUpgradeCode,squished_pc);
446     TRACE("squished (%s)\n", debugstr_w(squished_pc));
447
448     sprintfW(keypath,szInstaller_UpgradeCodes_fmt,squished_pc);
449
450     if (create)
451         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
452     else
453         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
454
455     return rc;
456 }
457
458 /*************************************************************************
459  *  MsiDecomposeDescriptorW   [MSI.@]
460  *
461  * Decomposes an MSI descriptor into product, feature and component parts.
462  * An MSI descriptor is a string of the form:
463  *   [base 85 guid] [feature code] '>' [base 85 guid]
464  *
465  * PARAMS
466  *   szDescriptor  [I]  the descriptor to decompose
467  *   szProduct     [O]  buffer of MAX_FEATURE_CHARS for the product guid
468  *   szFeature     [O]  buffer of MAX_FEATURE_CHARS for the feature code
469  *   szComponent   [O]  buffer of MAX_FEATURE_CHARS for the component guid
470  *   pUsed         [O]  the length of the descriptor
471  *
472  * RETURNS
473  *   ERROR_SUCCESS             if everything worked correctly
474  *   ERROR_INVALID_PARAMETER   if the descriptor was invalid
475  *
476  */
477 UINT WINAPI MsiDecomposeDescriptorW( LPCWSTR szDescriptor, LPWSTR szProduct,
478                 LPWSTR szFeature, LPWSTR szComponent, DWORD *pUsed )
479 {
480     UINT r, len;
481     LPWSTR p;
482     GUID product, component;
483
484     TRACE("%s %p %p %p %p\n", debugstr_w(szDescriptor), szProduct,
485           szFeature, szComponent, pUsed);
486
487     r = decode_base85_guid( szDescriptor, &product );
488     if( !r )
489         return ERROR_INVALID_PARAMETER;
490
491     TRACE("product %s\n", debugstr_guid( &product ));
492
493     p = strchrW(&szDescriptor[20],'>');
494     if( !p )
495         return ERROR_INVALID_PARAMETER;
496
497     len = (p - &szDescriptor[20]);
498     if( len > MAX_FEATURE_CHARS )
499         return ERROR_INVALID_PARAMETER;
500     memcpy( szFeature, &szDescriptor[20], len*sizeof(WCHAR) );
501     szFeature[len] = 0;
502
503     TRACE("feature %s\n", debugstr_w( szFeature ));
504
505     r = decode_base85_guid( p+1, &component );
506     if( !r )
507         return ERROR_INVALID_PARAMETER;
508
509     TRACE("component %s\n", debugstr_guid( &component ));
510
511     StringFromGUID2( &product, szProduct, MAX_FEATURE_CHARS+1 );
512     StringFromGUID2( &component, szComponent, MAX_FEATURE_CHARS+1 );
513     len = ( &p[21] - szDescriptor );
514
515     TRACE("length = %d\n", len);
516     *pUsed = len;
517
518     return ERROR_SUCCESS;
519 }
520
521 UINT WINAPI MsiDecomposeDescriptorA( LPCSTR szDescriptor, LPSTR szProduct,
522                 LPSTR szFeature, LPSTR szComponent, DWORD *pUsed )
523 {
524     WCHAR product[MAX_FEATURE_CHARS+1];
525     WCHAR feature[MAX_FEATURE_CHARS+1];
526     WCHAR component[MAX_FEATURE_CHARS+1];
527     LPWSTR str = NULL;
528     UINT r;
529
530     TRACE("%s %p %p %p %p\n", debugstr_a(szDescriptor), szProduct,
531           szFeature, szComponent, pUsed);
532
533     if( szDescriptor )
534     {
535         str = strdupAtoW( szDescriptor );
536         if( !str )
537             return ERROR_OUTOFMEMORY;
538     }
539
540     r = MsiDecomposeDescriptorW( str, product, feature, component, pUsed );
541
542     WideCharToMultiByte( CP_ACP, 0, product, MAX_FEATURE_CHARS+1,
543                          szProduct, MAX_FEATURE_CHARS+1, NULL, NULL );
544     WideCharToMultiByte( CP_ACP, 0, feature, MAX_FEATURE_CHARS+1,
545                          szFeature, MAX_FEATURE_CHARS+1, NULL, NULL );
546     WideCharToMultiByte( CP_ACP, 0, component, MAX_FEATURE_CHARS+1,
547                          szComponent, MAX_FEATURE_CHARS+1, NULL, NULL );
548
549     HeapFree( GetProcessHeap(), 0, str );
550
551     return r;
552 }
553
554 UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid)
555 {
556     DWORD r;
557     WCHAR szwGuid[GUID_SIZE];
558
559     TRACE("%ld %p\n",index,lpguid);
560     
561     if (NULL == lpguid)
562         return ERROR_INVALID_PARAMETER;
563     r = MsiEnumProductsW(index, szwGuid);
564     if( r == ERROR_SUCCESS )
565         WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
566
567     return r;
568 }
569
570 UINT WINAPI MsiEnumProductsW(DWORD index, LPWSTR lpguid)
571 {
572     HKEY hkeyFeatures = 0;
573     DWORD r;
574     WCHAR szKeyName[SQUISH_GUID_SIZE];
575
576     TRACE("%ld %p\n",index,lpguid);
577
578     if (NULL == lpguid)
579         return ERROR_INVALID_PARAMETER;
580
581     r = MSIREG_OpenFeatures(&hkeyFeatures);
582     if( r != ERROR_SUCCESS )
583         return ERROR_NO_MORE_ITEMS;
584
585     r = RegEnumKeyW(hkeyFeatures, index, szKeyName, SQUISH_GUID_SIZE);
586     if( r == ERROR_SUCCESS )
587         unsquash_guid(szKeyName, lpguid);
588     RegCloseKey(hkeyFeatures);
589
590     return r;
591 }
592
593 UINT WINAPI MsiEnumFeaturesA(LPCSTR szProduct, DWORD index, 
594       LPSTR szFeature, LPSTR szParent)
595 {
596     DWORD r;
597     WCHAR szwFeature[GUID_SIZE], szwParent[GUID_SIZE];
598     LPWSTR szwProduct = NULL;
599
600     TRACE("%s %ld %p %p\n",debugstr_a(szProduct),index,szFeature,szParent);
601
602     if( szProduct )
603     {
604         szwProduct = strdupAtoW( szProduct );
605         if( !szwProduct )
606             return ERROR_OUTOFMEMORY;
607     }
608
609     r = MsiEnumFeaturesW(szwProduct, index, szwFeature, szwParent);
610     if( r == ERROR_SUCCESS )
611     {
612         WideCharToMultiByte(CP_ACP, 0, szwFeature, -1,
613                             szFeature, GUID_SIZE, NULL, NULL);
614         WideCharToMultiByte(CP_ACP, 0, szwParent, -1,
615                             szParent, GUID_SIZE, NULL, NULL);
616     }
617
618     HeapFree( GetProcessHeap(), 0, szwProduct);
619
620     return r;
621 }
622
623 UINT WINAPI MsiEnumFeaturesW(LPCWSTR szProduct, DWORD index, 
624       LPWSTR szFeature, LPWSTR szParent)
625 {
626     HKEY hkeyProduct = 0;
627     DWORD r, sz;
628
629     TRACE("%s %ld %p %p\n",debugstr_w(szProduct),index,szFeature,szParent);
630
631     r = MSIREG_OpenFeaturesKey(szProduct,&hkeyProduct,FALSE);
632     if( r != ERROR_SUCCESS )
633         return ERROR_NO_MORE_ITEMS;
634
635     sz = GUID_SIZE;
636     r = RegEnumValueW(hkeyProduct, index, szFeature, &sz, NULL, NULL, NULL, NULL);
637     RegCloseKey(hkeyProduct);
638
639     return r;
640 }
641
642 UINT WINAPI MsiEnumComponentsA(DWORD index, LPSTR lpguid)
643 {
644     DWORD r;
645     WCHAR szwGuid[GUID_SIZE];
646
647     TRACE("%ld %p\n",index,lpguid);
648
649     r = MsiEnumComponentsW(index, szwGuid);
650     if( r == ERROR_SUCCESS )
651         WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
652
653     return r;
654 }
655
656 UINT WINAPI MsiEnumComponentsW(DWORD index, LPWSTR lpguid)
657 {
658     HKEY hkeyComponents = 0;
659     DWORD r;
660     WCHAR szKeyName[SQUISH_GUID_SIZE];
661
662     TRACE("%ld %p\n",index,lpguid);
663
664     r = MSIREG_OpenComponents(&hkeyComponents);
665     if( r != ERROR_SUCCESS )
666         return ERROR_NO_MORE_ITEMS;
667
668     r = RegEnumKeyW(hkeyComponents, index, szKeyName, SQUISH_GUID_SIZE);
669     if( r == ERROR_SUCCESS )
670         unsquash_guid(szKeyName, lpguid);
671     RegCloseKey(hkeyComponents);
672
673     return r;
674 }
675
676 UINT WINAPI MsiEnumClientsA(LPCSTR szComponent, DWORD index, LPSTR szProduct)
677 {
678     DWORD r;
679     WCHAR szwProduct[GUID_SIZE];
680     LPWSTR szwComponent = NULL;
681
682     TRACE("%s %ld %p\n",debugstr_a(szComponent),index,szProduct);
683
684     if( szComponent )
685     {
686         szwComponent = strdupAtoW( szComponent );
687         if( !szwComponent )
688             return ERROR_OUTOFMEMORY;
689     }
690
691     r = MsiEnumClientsW(szComponent?szwComponent:NULL, index, szwProduct);
692     if( r == ERROR_SUCCESS )
693     {
694         WideCharToMultiByte(CP_ACP, 0, szwProduct, -1,
695                             szProduct, GUID_SIZE, NULL, NULL);
696     }
697
698     HeapFree( GetProcessHeap(), 0, szwComponent);
699
700     return r;
701 }
702
703 UINT WINAPI MsiEnumClientsW(LPCWSTR szComponent, DWORD index, LPWSTR szProduct)
704 {
705     HKEY hkeyComp = 0;
706     DWORD r, sz;
707     WCHAR szValName[SQUISH_GUID_SIZE];
708
709     TRACE("%s %ld %p\n",debugstr_w(szComponent),index,szProduct);
710
711     r = MSIREG_OpenComponentsKey(szComponent,&hkeyComp,FALSE);
712     if( r != ERROR_SUCCESS )
713         return ERROR_NO_MORE_ITEMS;
714
715     sz = SQUISH_GUID_SIZE;
716     r = RegEnumValueW(hkeyComp, index, szValName, &sz, NULL, NULL, NULL, NULL);
717     if( r == ERROR_SUCCESS )
718         unsquash_guid(szValName, szProduct);
719
720     RegCloseKey(hkeyComp);
721
722     return r;
723 }
724
725 /*************************************************************************
726  *  MsiEnumComponentQualifiersA [MSI.@]
727  *
728  */
729 UINT WINAPI MsiEnumComponentQualifiersA( LPSTR szComponent, DWORD iIndex,
730                 LPSTR lpQualifierBuf, DWORD* pcchQualifierBuf,
731                 LPSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf)
732 {
733     LPWSTR szwComponent;
734     LPWSTR lpwQualifierBuf;
735     DWORD pcchwQualifierBuf;
736     LPWSTR lpwApplicationDataBuf;
737     DWORD pcchwApplicationDataBuf;
738     DWORD rc;
739     DWORD length;
740
741     TRACE("%s %08lx %p %p %p %p\n", debugstr_a(szComponent), iIndex,
742           lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf,
743           pcchApplicationDataBuf);
744
745     szwComponent = strdupAtoW(szComponent);
746
747     if (lpQualifierBuf)
748         lpwQualifierBuf = HeapAlloc(GetProcessHeap(),0, (*pcchQualifierBuf) * 
749                         sizeof(WCHAR));
750     else
751         lpwQualifierBuf = NULL;
752
753     if (pcchQualifierBuf)
754         pcchwQualifierBuf = *pcchQualifierBuf;
755     else
756         pcchwQualifierBuf = 0;
757
758     if (lpApplicationDataBuf)
759        lpwApplicationDataBuf = HeapAlloc(GetProcessHeap(),0 ,
760                             (*pcchApplicationDataBuf) * sizeof(WCHAR));
761     else
762         lpwApplicationDataBuf = NULL;
763
764     if (pcchApplicationDataBuf)
765         pcchwApplicationDataBuf = *pcchApplicationDataBuf;
766     else
767         pcchwApplicationDataBuf = 0;
768
769     rc = MsiEnumComponentQualifiersW( szwComponent, iIndex, lpwQualifierBuf, 
770                     &pcchwQualifierBuf, lpwApplicationDataBuf,
771                     &pcchwApplicationDataBuf);
772
773     /*
774      * A bit of wizardry to report back the length without the null.
775      * just in case the buffer is too small and is filled.
776      */
777     if (lpQualifierBuf)
778     {
779         length = WideCharToMultiByte(CP_ACP, 0, lpwQualifierBuf, -1,
780                         lpQualifierBuf, *pcchQualifierBuf, NULL, NULL); 
781
782         if (*pcchQualifierBuf == length && lpQualifierBuf[length-1])
783             *pcchQualifierBuf = length;
784         else
785             *pcchQualifierBuf = length - 1;
786     }
787     if (lpApplicationDataBuf)
788     {
789         length = WideCharToMultiByte(CP_ACP, 0,
790                         lpwApplicationDataBuf, -1, lpApplicationDataBuf,
791                         *pcchApplicationDataBuf, NULL, NULL); 
792
793         if (*pcchApplicationDataBuf == length && lpApplicationDataBuf[length-1])
794             *pcchApplicationDataBuf = length;
795         else
796             *pcchApplicationDataBuf = length - 1;
797     }
798
799     HeapFree(GetProcessHeap(),0,lpwApplicationDataBuf);
800     HeapFree(GetProcessHeap(),0,lpwQualifierBuf);
801     HeapFree(GetProcessHeap(),0,szwComponent);
802
803     return rc;
804 }
805
806 /*************************************************************************
807  *  MsiEnumComponentQualifiersW [MSI.@]
808  *
809  */
810 UINT WINAPI MsiEnumComponentQualifiersW( LPWSTR szComponent, DWORD iIndex,
811                 LPWSTR lpQualifierBuf, DWORD* pcchQualifierBuf,
812                 LPWSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf )
813 {
814     UINT rc;
815     HKEY key;
816     DWORD actual_pcchQualifierBuf = 0;
817     DWORD actual_pcchApplicationDataBuf = 0;
818     LPWSTR full_buffer = NULL;
819     DWORD full_buffer_size = 0;
820     LPWSTR ptr;
821
822     TRACE("%s %08lx %p %p %p %p\n", debugstr_w(szComponent), iIndex,
823           lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf,
824           pcchApplicationDataBuf);
825
826     if (pcchQualifierBuf)
827         actual_pcchQualifierBuf = *pcchQualifierBuf * sizeof(WCHAR);
828     if (pcchApplicationDataBuf)
829         actual_pcchApplicationDataBuf = *pcchApplicationDataBuf * sizeof(WCHAR);
830     
831     rc = MSIREG_OpenUserComponentsKey(szComponent, &key, FALSE);
832     if (rc != ERROR_SUCCESS)
833         return ERROR_UNKNOWN_COMPONENT;
834
835     full_buffer_size = (52 * sizeof(WCHAR)) + actual_pcchApplicationDataBuf;
836     full_buffer = HeapAlloc(GetProcessHeap(),0,full_buffer_size);
837     
838     rc = RegEnumValueW(key, iIndex, lpQualifierBuf, pcchQualifierBuf, NULL, 
839                     NULL, (LPBYTE)full_buffer, &full_buffer_size);
840
841     RegCloseKey(key);
842
843     if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
844     {
845         if (lpApplicationDataBuf && pcchApplicationDataBuf)
846         {
847             ptr = full_buffer;
848             /* Skip the first guid */
849             ptr += 21;
850     
851             /* Skip the name and the component guid if it exists */
852             if (strchrW(ptr,'<'))
853                 ptr = strchrW(ptr,'<');
854             else 
855                 ptr = strchrW(ptr,'>') + 21;
856
857             lstrcpynW(lpApplicationDataBuf,ptr,*pcchApplicationDataBuf);
858             *pcchApplicationDataBuf = strlenW(ptr);
859         }
860         if (lpQualifierBuf && pcchQualifierBuf)
861             *pcchQualifierBuf /= sizeof(WCHAR); 
862         TRACE("Providing %s and %s\n", debugstr_w(lpQualifierBuf), 
863                         debugstr_w(lpApplicationDataBuf));
864     }
865
866     return rc;
867 }
868
869 /*************************************************************************
870  *  MsiEnumRelatedProductsW   [MSI.@]
871  *
872  */
873 UINT WINAPI MsiEnumRelatedProductsW(LPCWSTR szUpgradeCode, DWORD dwReserved,
874                                     DWORD iProductIndex, LPWSTR lpProductBuf)
875 {
876     UINT r;
877     HKEY hkey;
878     WCHAR szKeyName[SQUISH_GUID_SIZE];
879
880     TRACE("%s %lu %lu %p\n", debugstr_w(szUpgradeCode), dwReserved,
881           iProductIndex, lpProductBuf);
882
883     if (NULL == szUpgradeCode)
884         return ERROR_INVALID_PARAMETER;
885     if (NULL == lpProductBuf)
886         return ERROR_INVALID_PARAMETER;
887
888     r = MSIREG_OpenUpgradeCodesKey(szUpgradeCode, &hkey, FALSE);
889     if (r != ERROR_SUCCESS)
890         return ERROR_NO_MORE_ITEMS;
891
892     r = RegEnumKeyW(hkey, iProductIndex, szKeyName, SQUISH_GUID_SIZE);
893     if( r == ERROR_SUCCESS )
894         unsquash_guid(szKeyName, lpProductBuf);
895     RegCloseKey(hkey);
896
897     return r;
898 }
899
900 /*************************************************************************
901  *  MsiEnumRelatedProductsA   [MSI.@]
902  *
903  */
904 UINT WINAPI MsiEnumRelatedProductsA(LPCSTR szUpgradeCode, DWORD dwReserved,
905                                     DWORD iProductIndex, LPSTR lpProductBuf)
906 {
907     LPWSTR szwUpgradeCode = NULL;
908     WCHAR productW[GUID_SIZE];
909     UINT r;
910
911     TRACE("%s %lu %lu %p\n", debugstr_a(szUpgradeCode), dwReserved,
912           iProductIndex, lpProductBuf);
913
914     if (szUpgradeCode)
915     {
916         szwUpgradeCode = strdupAtoW( szUpgradeCode );
917         if( !szwUpgradeCode )
918             return ERROR_OUTOFMEMORY;
919     }
920
921     r = MsiEnumRelatedProductsW( szwUpgradeCode, dwReserved,
922                                  iProductIndex, productW );
923     if (r == ERROR_SUCCESS)
924     {
925         WideCharToMultiByte( CP_ACP, 0, productW, GUID_SIZE,
926                              lpProductBuf, GUID_SIZE, NULL, NULL );
927     }
928     HeapFree(GetProcessHeap(), 0, szwUpgradeCode);
929     return r;
930 }