msi: Represent table data as bytes instead of shorts.
[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
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_fmt[] = {
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','\\',
134 '%','s',0};
135
136 static const WCHAR szInstaller_UserUpgradeCodes_fmt[] = {
137 'S','o','f','t','w','a','r','e','\\',
138 'M','i','c','r','o','s','o','f','t','\\',
139 'I','n','s','t','a','l','l','e','r','\\',
140 'U','p','g','r','a','d','e','C','o','d','e','s','\\',
141 '%','s',0};
142
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=1;
179     GUID guid;
180
181     if (FAILED(CLSIDFromString((LPOLESTR)in, &guid)))
182         return FALSE;
183
184     for(i=0; i<8; i++)
185         out[7-i] = in[n++];
186     n++;
187     for(i=0; i<4; i++)
188         out[11-i] = in[n++];
189     n++;
190     for(i=0; i<4; i++)
191         out[15-i] = in[n++];
192     n++;
193     for(i=0; i<2; i++)
194     {
195         out[17+i*2] = in[n++];
196         out[16+i*2] = in[n++];
197     }
198     n++;
199     for( ; i<8; i++)
200     {
201         out[17+i*2] = in[n++];
202         out[16+i*2] = in[n++];
203     }
204     out[32]=0;
205     return TRUE;
206 }
207
208
209 /* tables for encoding and decoding base85 */
210 static const unsigned char table_dec85[0x80] = {
211 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
212 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
213 0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff,
214 0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17,
215 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
216 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36,
217 0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
218 0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff,
219 };
220
221 static const char table_enc85[] =
222 "!$%&'()*+,-.0123456789=?@ABCDEFGHIJKLMNO"
223 "PQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwx"
224 "yz{}~";
225
226 /*
227  *  Converts a base85 encoded guid into a GUID pointer
228  *  Base85 encoded GUIDs should be 20 characters long.
229  *
230  *  returns TRUE if successful, FALSE if not
231  */
232 BOOL decode_base85_guid( LPCWSTR str, GUID *guid )
233 {
234     DWORD i, val = 0, base = 1, *p;
235
236     if (!str)
237         return FALSE;
238
239     p = (DWORD*) guid;
240     for( i=0; i<20; i++ )
241     {
242         if( (i%5) == 0 )
243         {
244             val = 0;
245             base = 1;
246         }
247         val += table_dec85[str[i]] * base;
248         if( str[i] >= 0x80 )
249             return FALSE;
250         if( table_dec85[str[i]] == 0xff )
251             return FALSE;
252         if( (i%5) == 4 )
253             p[i/5] = val;
254         base *= 85;
255     }
256     return TRUE;
257 }
258
259 /*
260  *  Encodes a base85 guid given a GUID pointer
261  *  Caller should provide a 21 character buffer for the encoded string.
262  *
263  *  returns TRUE if successful, FALSE if not
264  */
265 BOOL encode_base85_guid( GUID *guid, LPWSTR str )
266 {
267     unsigned int x, *p, i;
268
269     p = (unsigned int*) guid;
270     for( i=0; i<4; i++ )
271     {
272         x = p[i];
273         *str++ = table_enc85[x%85];
274         x = x/85;
275         *str++ = table_enc85[x%85];
276         x = x/85;
277         *str++ = table_enc85[x%85];
278         x = x/85;
279         *str++ = table_enc85[x%85];
280         x = x/85;
281         *str++ = table_enc85[x%85];
282     }
283     *str = 0;
284
285     return TRUE;
286 }
287
288 DWORD msi_version_str_to_dword(LPCWSTR p)
289 {
290     DWORD major, minor = 0, build = 0, version = 0;
291
292     if (!p)
293         return version;
294
295     major = atoiW(p);
296
297     p = strchrW(p, '.');
298     if (p)
299     {
300         minor = atoiW(p+1);
301         p = strchrW(p+1, '.');
302         if (p)
303             build = atoiW(p+1);
304     }
305
306     return MAKELONG(build, MAKEWORD(minor, major));
307 }
308
309 LPWSTR msi_version_dword_to_str(DWORD version)
310 {
311     const WCHAR fmt[] = { '%','u','.','%','u','.','%','u',0 };
312     LPWSTR str = msi_alloc(20);
313     sprintfW(str, fmt,
314              (version&0xff000000)>>24,
315              (version&0x00ff0000)>>16,
316               version&0x0000ffff);
317     return str;
318 }
319
320 LONG msi_reg_set_val_str( HKEY hkey, LPCWSTR name, LPCWSTR value )
321 {
322     DWORD len = value ? (lstrlenW(value) + 1) * sizeof (WCHAR) : 0;
323     return RegSetValueExW( hkey, name, 0, REG_SZ, (const BYTE *)value, len );
324 }
325
326 LONG msi_reg_set_val_multi_str( HKEY hkey, LPCWSTR name, LPCWSTR value )
327 {
328     LPCWSTR p = value;
329     while (*p) p += lstrlenW(p) + 1;
330     return RegSetValueExW( hkey, name, 0, REG_MULTI_SZ,
331                            (const BYTE *)value, (p + 1 - value) * sizeof(WCHAR) );
332 }
333
334 LONG msi_reg_set_val_dword( HKEY hkey, LPCWSTR name, DWORD val )
335 {
336     return RegSetValueExW( hkey, name, 0, REG_DWORD, (LPBYTE)&val, sizeof (DWORD) );
337 }
338
339 LONG msi_reg_set_subkey_val( HKEY hkey, LPCWSTR path, LPCWSTR name, LPCWSTR val )
340 {
341     HKEY hsubkey = 0;
342     LONG r;
343
344     r = RegCreateKeyW( hkey, path, &hsubkey );
345     if (r != ERROR_SUCCESS)
346         return r;
347     r = msi_reg_set_val_str( hsubkey, name, val );
348     RegCloseKey( hsubkey );
349     return r;
350 }
351
352 LPWSTR msi_reg_get_val_str( HKEY hkey, LPCWSTR name )
353 {
354     DWORD len = 0;
355     LPWSTR val;
356     LONG r;
357
358     r = RegQueryValueExW(hkey, name, NULL, NULL, NULL, &len);
359     if (r != ERROR_SUCCESS)
360         return NULL;
361
362     len += sizeof (WCHAR);
363     val = msi_alloc( len );
364     if (!val)
365         return NULL;
366     val[0] = 0;
367     RegQueryValueExW(hkey, name, NULL, NULL, (LPBYTE) val, &len);
368     return val;
369 }
370
371 BOOL msi_reg_get_val_dword( HKEY hkey, LPCWSTR name, DWORD *val)
372 {
373     DWORD type, len = sizeof (DWORD);
374     LONG r = RegQueryValueExW(hkey, name, NULL, &type, (LPBYTE) val, &len);
375     return r == ERROR_SUCCESS && type == REG_DWORD;
376 }
377
378 UINT MSIREG_OpenUninstallKey(LPCWSTR szProduct, HKEY* key, BOOL create)
379 {
380     UINT rc;
381     WCHAR keypath[0x200];
382     TRACE("%s\n",debugstr_w(szProduct));
383
384     sprintfW(keypath,szUninstall_fmt,szProduct);
385
386     if (create)
387         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
388     else
389         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
390
391     return rc;
392 }
393
394 UINT MSIREG_OpenUserProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create)
395 {
396     UINT rc;
397     WCHAR squished_pc[GUID_SIZE];
398     WCHAR keypath[0x200];
399
400     TRACE("%s\n",debugstr_w(szProduct));
401     squash_guid(szProduct,squished_pc);
402     TRACE("squished (%s)\n", debugstr_w(squished_pc));
403
404     sprintfW(keypath,szUserProduct_fmt,squished_pc);
405
406     if (create)
407         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
408     else
409         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
410
411     return rc;
412 }
413
414 UINT MSIREG_OpenUserFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create)
415 {
416     UINT rc;
417     WCHAR squished_pc[GUID_SIZE];
418     WCHAR keypath[0x200];
419
420     TRACE("%s\n",debugstr_w(szProduct));
421     squash_guid(szProduct,squished_pc);
422     TRACE("squished (%s)\n", debugstr_w(squished_pc));
423
424     sprintfW(keypath,szUserFeatures_fmt,squished_pc);
425
426     if (create)
427         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
428     else
429         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
430
431     return rc;
432 }
433
434 UINT MSIREG_OpenFeatures(HKEY* key)
435 {
436     return RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller_Features,key);
437 }
438
439 UINT MSIREG_OpenFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create)
440 {
441     UINT rc;
442     WCHAR squished_pc[GUID_SIZE];
443     WCHAR keypath[0x200];
444
445     TRACE("%s\n",debugstr_w(szProduct));
446     squash_guid(szProduct,squished_pc);
447     TRACE("squished (%s)\n", debugstr_w(squished_pc));
448
449     sprintfW(keypath,szInstaller_Features_fmt,squished_pc);
450
451     if (create)
452         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
453     else
454         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
455
456     return rc;
457 }
458
459 UINT MSIREG_OpenComponents(HKEY* key)
460 {
461     return RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller_Components,key);
462 }
463
464 UINT MSIREG_OpenComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create)
465 {
466     UINT rc;
467     WCHAR squished_cc[GUID_SIZE];
468     WCHAR keypath[0x200];
469
470     TRACE("%s\n",debugstr_w(szComponent));
471     squash_guid(szComponent,squished_cc);
472     TRACE("squished (%s)\n", debugstr_w(squished_cc));
473
474     sprintfW(keypath,szInstaller_Components_fmt,squished_cc);
475
476     if (create)
477         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
478     else
479         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
480
481     return rc;
482 }
483
484 UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create)
485 {
486     UINT rc;
487     WCHAR squished_cc[GUID_SIZE];
488     WCHAR keypath[0x200];
489
490     TRACE("%s\n",debugstr_w(szComponent));
491     squash_guid(szComponent,squished_cc);
492     TRACE("squished (%s)\n", debugstr_w(squished_cc));
493
494     sprintfW(keypath,szUser_Components_fmt,squished_cc);
495
496     if (create)
497         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
498     else
499         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
500
501     return rc;
502 }
503
504 UINT MSIREG_OpenProducts(HKEY* key)
505 {
506     return RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller_Products,key);
507 }
508
509 UINT MSIREG_OpenProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create)
510 {
511     UINT rc;
512     WCHAR squished_pc[GUID_SIZE];
513     WCHAR keypath[0x200];
514
515     TRACE("%s\n",debugstr_w(szProduct));
516     squash_guid(szProduct,squished_pc);
517     TRACE("squished (%s)\n", debugstr_w(squished_pc));
518
519     sprintfW(keypath,szInstaller_Products_fmt,squished_pc);
520
521     if (create)
522         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
523     else
524         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
525
526     return rc;
527 }
528
529 UINT MSIREG_OpenUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create)
530 {
531     UINT rc;
532     WCHAR squished_pc[GUID_SIZE];
533     WCHAR keypath[0x200];
534
535     TRACE("%s\n",debugstr_w(szUpgradeCode));
536     squash_guid(szUpgradeCode,squished_pc);
537     TRACE("squished (%s)\n", debugstr_w(squished_pc));
538
539     sprintfW(keypath,szInstaller_UpgradeCodes_fmt,squished_pc);
540
541     if (create)
542         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
543     else
544         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
545
546     return rc;
547 }
548
549 UINT MSIREG_OpenUserUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create)
550 {
551     UINT rc;
552     WCHAR squished_pc[GUID_SIZE];
553     WCHAR keypath[0x200];
554
555     TRACE("%s\n",debugstr_w(szUpgradeCode));
556     squash_guid(szUpgradeCode,squished_pc);
557     TRACE("squished (%s)\n", debugstr_w(squished_pc));
558
559     sprintfW(keypath,szInstaller_UserUpgradeCodes_fmt,squished_pc);
560
561     if (create)
562         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
563     else
564         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
565
566     return rc;
567 }
568
569
570 /*************************************************************************
571  *  MsiDecomposeDescriptorW   [MSI.@]
572  *
573  * Decomposes an MSI descriptor into product, feature and component parts.
574  * An MSI descriptor is a string of the form:
575  *   [base 85 guid] [feature code] '>' [base 85 guid]
576  *
577  * PARAMS
578  *   szDescriptor  [I]  the descriptor to decompose
579  *   szProduct     [O]  buffer of MAX_FEATURE_CHARS+1 for the product guid
580  *   szFeature     [O]  buffer of MAX_FEATURE_CHARS+1 for the feature code
581  *   szComponent   [O]  buffer of MAX_FEATURE_CHARS+1 for the component guid
582  *   pUsed         [O]  the length of the descriptor
583  *
584  * RETURNS
585  *   ERROR_SUCCESS             if everything worked correctly
586  *   ERROR_INVALID_PARAMETER   if the descriptor was invalid
587  *
588  */
589 UINT WINAPI MsiDecomposeDescriptorW( LPCWSTR szDescriptor, LPWSTR szProduct,
590                 LPWSTR szFeature, LPWSTR szComponent, DWORD *pUsed )
591 {
592     UINT r, len;
593     LPWSTR p;
594     GUID product, component;
595
596     TRACE("%s %p %p %p %p\n", debugstr_w(szDescriptor), szProduct,
597           szFeature, szComponent, pUsed);
598
599     r = decode_base85_guid( szDescriptor, &product );
600     if( !r )
601         return ERROR_INVALID_PARAMETER;
602
603     TRACE("product %s\n", debugstr_guid( &product ));
604
605     p = strchrW(&szDescriptor[20],'>');
606     if( !p )
607         return ERROR_INVALID_PARAMETER;
608
609     len = (p - &szDescriptor[20]);
610     if( len > MAX_FEATURE_CHARS )
611         return ERROR_INVALID_PARAMETER;
612
613     TRACE("feature %s\n", debugstr_wn( &szDescriptor[20], len ));
614
615     r = decode_base85_guid( p+1, &component );
616     if( !r )
617         return ERROR_INVALID_PARAMETER;
618
619     TRACE("component %s\n", debugstr_guid( &component ));
620
621     if (szProduct)
622         StringFromGUID2( &product, szProduct, MAX_FEATURE_CHARS+1 );
623     if (szComponent)
624         StringFromGUID2( &component, szComponent, MAX_FEATURE_CHARS+1 );
625     if (szFeature)
626     {
627         memcpy( szFeature, &szDescriptor[20], len*sizeof(WCHAR) );
628         szFeature[len] = 0;
629     }
630     len = ( &p[21] - szDescriptor );
631
632     TRACE("length = %d\n", len);
633     *pUsed = len;
634
635     return ERROR_SUCCESS;
636 }
637
638 UINT WINAPI MsiDecomposeDescriptorA( LPCSTR szDescriptor, LPSTR szProduct,
639                 LPSTR szFeature, LPSTR szComponent, DWORD *pUsed )
640 {
641     WCHAR product[MAX_FEATURE_CHARS+1];
642     WCHAR feature[MAX_FEATURE_CHARS+1];
643     WCHAR component[MAX_FEATURE_CHARS+1];
644     LPWSTR str = NULL, p = NULL, f = NULL, c = NULL;
645     UINT r;
646
647     TRACE("%s %p %p %p %p\n", debugstr_a(szDescriptor), szProduct,
648           szFeature, szComponent, pUsed);
649
650     str = strdupAtoW( szDescriptor );
651     if( szDescriptor && !str )
652         return ERROR_OUTOFMEMORY;
653
654     if (szProduct)
655         p = product;
656     if (szFeature)
657         f = feature;
658     if (szComponent)
659         c = component;
660
661     r = MsiDecomposeDescriptorW( str, p, f, c, pUsed );
662
663     if (r == ERROR_SUCCESS)
664     {
665         WideCharToMultiByte( CP_ACP, 0, p, -1,
666                              szProduct, MAX_FEATURE_CHARS+1, NULL, NULL );
667         WideCharToMultiByte( CP_ACP, 0, f, -1,
668                              szFeature, MAX_FEATURE_CHARS+1, NULL, NULL );
669         WideCharToMultiByte( CP_ACP, 0, c, -1,
670                              szComponent, MAX_FEATURE_CHARS+1, NULL, NULL );
671     }
672
673     msi_free( str );
674
675     return r;
676 }
677
678 UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid)
679 {
680     DWORD r;
681     WCHAR szwGuid[GUID_SIZE];
682
683     TRACE("%d %p\n", index, lpguid);
684
685     if (NULL == lpguid)
686         return ERROR_INVALID_PARAMETER;
687     r = MsiEnumProductsW(index, szwGuid);
688     if( r == ERROR_SUCCESS )
689         WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
690
691     return r;
692 }
693
694 UINT WINAPI MsiEnumProductsW(DWORD index, LPWSTR lpguid)
695 {
696     HKEY hkeyProducts = 0;
697     DWORD r;
698     WCHAR szKeyName[SQUISH_GUID_SIZE];
699
700     TRACE("%d %p\n", index, lpguid);
701
702     if (NULL == lpguid)
703         return ERROR_INVALID_PARAMETER;
704
705     r = MSIREG_OpenProducts(&hkeyProducts);
706     if( r != ERROR_SUCCESS )
707         return ERROR_NO_MORE_ITEMS;
708
709     r = RegEnumKeyW(hkeyProducts, index, szKeyName, SQUISH_GUID_SIZE);
710     if( r == ERROR_SUCCESS )
711         unsquash_guid(szKeyName, lpguid);
712     RegCloseKey(hkeyProducts);
713
714     return r;
715 }
716
717 UINT WINAPI MsiEnumFeaturesA(LPCSTR szProduct, DWORD index, 
718       LPSTR szFeature, LPSTR szParent)
719 {
720     DWORD r;
721     WCHAR szwFeature[GUID_SIZE], szwParent[GUID_SIZE];
722     LPWSTR szwProduct = NULL;
723
724     TRACE("%s %d %p %p\n", debugstr_a(szProduct), index, szFeature, szParent);
725
726     if( szProduct )
727     {
728         szwProduct = strdupAtoW( szProduct );
729         if( !szwProduct )
730             return ERROR_OUTOFMEMORY;
731     }
732
733     r = MsiEnumFeaturesW(szwProduct, index, szwFeature, szwParent);
734     if( r == ERROR_SUCCESS )
735     {
736         WideCharToMultiByte(CP_ACP, 0, szwFeature, -1,
737                             szFeature, GUID_SIZE, NULL, NULL);
738         WideCharToMultiByte(CP_ACP, 0, szwParent, -1,
739                             szParent, GUID_SIZE, NULL, NULL);
740     }
741
742     msi_free( szwProduct);
743
744     return r;
745 }
746
747 UINT WINAPI MsiEnumFeaturesW(LPCWSTR szProduct, DWORD index, 
748       LPWSTR szFeature, LPWSTR szParent)
749 {
750     HKEY hkeyProduct = 0;
751     DWORD r, sz;
752
753     TRACE("%s %d %p %p\n", debugstr_w(szProduct), index, szFeature, szParent);
754
755     if( !szProduct )
756         return ERROR_INVALID_PARAMETER;
757
758     r = MSIREG_OpenFeaturesKey(szProduct,&hkeyProduct,FALSE);
759     if( r != ERROR_SUCCESS )
760         return ERROR_NO_MORE_ITEMS;
761
762     sz = GUID_SIZE;
763     r = RegEnumValueW(hkeyProduct, index, szFeature, &sz, NULL, NULL, NULL, NULL);
764     RegCloseKey(hkeyProduct);
765
766     return r;
767 }
768
769 UINT WINAPI MsiEnumComponentsA(DWORD index, LPSTR lpguid)
770 {
771     DWORD r;
772     WCHAR szwGuid[GUID_SIZE];
773
774     TRACE("%d %p\n", index, lpguid);
775
776     r = MsiEnumComponentsW(index, szwGuid);
777     if( r == ERROR_SUCCESS )
778         WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
779
780     return r;
781 }
782
783 UINT WINAPI MsiEnumComponentsW(DWORD index, LPWSTR lpguid)
784 {
785     HKEY hkeyComponents = 0;
786     DWORD r;
787     WCHAR szKeyName[SQUISH_GUID_SIZE];
788
789     TRACE("%d %p\n", index, lpguid);
790
791     r = MSIREG_OpenComponents(&hkeyComponents);
792     if( r != ERROR_SUCCESS )
793         return ERROR_NO_MORE_ITEMS;
794
795     r = RegEnumKeyW(hkeyComponents, index, szKeyName, SQUISH_GUID_SIZE);
796     if( r == ERROR_SUCCESS )
797         unsquash_guid(szKeyName, lpguid);
798     RegCloseKey(hkeyComponents);
799
800     return r;
801 }
802
803 UINT WINAPI MsiEnumClientsA(LPCSTR szComponent, DWORD index, LPSTR szProduct)
804 {
805     DWORD r;
806     WCHAR szwProduct[GUID_SIZE];
807     LPWSTR szwComponent = NULL;
808
809     TRACE("%s %d %p\n", debugstr_a(szComponent), index, szProduct);
810
811     if( szComponent )
812     {
813         szwComponent = strdupAtoW( szComponent );
814         if( !szwComponent )
815             return ERROR_OUTOFMEMORY;
816     }
817
818     r = MsiEnumClientsW(szComponent?szwComponent:NULL, index, szwProduct);
819     if( r == ERROR_SUCCESS )
820     {
821         WideCharToMultiByte(CP_ACP, 0, szwProduct, -1,
822                             szProduct, GUID_SIZE, NULL, NULL);
823     }
824
825     msi_free( szwComponent);
826
827     return r;
828 }
829
830 UINT WINAPI MsiEnumClientsW(LPCWSTR szComponent, DWORD index, LPWSTR szProduct)
831 {
832     HKEY hkeyComp = 0;
833     DWORD r, sz;
834     WCHAR szValName[SQUISH_GUID_SIZE];
835
836     TRACE("%s %d %p\n", debugstr_w(szComponent), index, szProduct);
837
838     r = MSIREG_OpenComponentsKey(szComponent,&hkeyComp,FALSE);
839     if( r != ERROR_SUCCESS )
840         return ERROR_NO_MORE_ITEMS;
841
842     sz = SQUISH_GUID_SIZE;
843     r = RegEnumValueW(hkeyComp, index, szValName, &sz, NULL, NULL, NULL, NULL);
844     if( r == ERROR_SUCCESS )
845         unsquash_guid(szValName, szProduct);
846
847     RegCloseKey(hkeyComp);
848
849     return r;
850 }
851
852 static UINT WINAPI MSI_EnumComponentQualifiers( LPCWSTR szComponent, DWORD iIndex,
853                 awstring *lpQualBuf, DWORD* pcchQual,
854                 awstring *lpAppBuf, DWORD* pcchAppBuf )
855 {
856     DWORD name_sz, val_sz, name_max, val_max, type, ofs;
857     LPWSTR name = NULL, val = NULL;
858     UINT r, r2;
859     HKEY key;
860
861     TRACE("%s %08x %p %p %p %p\n", debugstr_w(szComponent), iIndex,
862           lpQualBuf, pcchQual, lpAppBuf, pcchAppBuf);
863
864     if (!szComponent)
865         return ERROR_INVALID_PARAMETER;
866
867     r = MSIREG_OpenUserComponentsKey( szComponent, &key, FALSE );
868     if (r != ERROR_SUCCESS)
869         return ERROR_UNKNOWN_COMPONENT;
870
871     /* figure out how big the name is we want to return */
872     name_max = 0x10;
873     r = ERROR_OUTOFMEMORY;
874     name = msi_alloc( name_max * sizeof(WCHAR) );
875     if (!name)
876         goto end;
877
878     val_max = 0x10;
879     r = ERROR_OUTOFMEMORY;
880     val = msi_alloc( val_max );
881     if (!val)
882         goto end;
883
884     /* loop until we allocate enough memory */
885     while (1)
886     {
887         name_sz = name_max;
888         val_sz = val_max;
889         r = RegEnumValueW( key, iIndex, name, &name_sz,
890                            NULL, &type, (LPBYTE)val, &val_sz );
891         if (r == ERROR_SUCCESS)
892             break;
893         if (r != ERROR_MORE_DATA)
894             goto end;
895  
896         if (type != REG_MULTI_SZ)
897         {
898             ERR("component data has wrong type (%d)\n", type);
899             goto end;
900         }
901
902         r = ERROR_OUTOFMEMORY;
903         if ((name_sz+1) >= name_max)
904         {
905             name_max *= 2;
906             msi_free( name );
907             name = msi_alloc( name_max * sizeof (WCHAR) );
908             if (!name)
909                 goto end;
910             continue;
911         }
912         if (val_sz > val_max)
913         {
914             val_max = val_sz + sizeof (WCHAR);
915             msi_free( val );
916             val = msi_alloc( val_max * sizeof (WCHAR) );
917             if (!val)
918                 goto end;
919             continue;
920         }
921         ERR("should be enough data, but isn't %d %d\n", name_sz, val_sz );
922         goto end;
923     }
924
925     ofs = 0;
926     r = MsiDecomposeDescriptorW( val, NULL, NULL, NULL, &ofs );
927     if (r != ERROR_SUCCESS)
928         goto end;
929
930     TRACE("Providing %s and %s\n", debugstr_w(name), debugstr_w(val+ofs));
931
932     r = msi_strcpy_to_awstring( name, lpQualBuf, pcchQual );
933     r2 = msi_strcpy_to_awstring( val+ofs, lpAppBuf, pcchAppBuf );
934
935     if (r2 != ERROR_SUCCESS)
936         r = r2;
937
938 end:
939     msi_free(val);
940     msi_free(name);
941     RegCloseKey(key);
942
943     return r;
944 }
945
946 /*************************************************************************
947  *  MsiEnumComponentQualifiersA [MSI.@]
948  */
949 UINT WINAPI MsiEnumComponentQualifiersA( LPCSTR szComponent, DWORD iIndex,
950                 LPSTR lpQualifierBuf, DWORD* pcchQualifierBuf,
951                 LPSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf )
952 {
953     awstring qual, appdata;
954     LPWSTR comp;
955     UINT r;
956
957     TRACE("%s %08x %p %p %p %p\n", debugstr_a(szComponent), iIndex,
958           lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf,
959           pcchApplicationDataBuf);
960
961     comp = strdupAtoW( szComponent );
962     if (szComponent && !comp)
963         return ERROR_OUTOFMEMORY;
964
965     qual.unicode = FALSE;
966     qual.str.a = lpQualifierBuf;
967
968     appdata.unicode = FALSE;
969     appdata.str.a = lpApplicationDataBuf;
970
971     r = MSI_EnumComponentQualifiers( comp, iIndex,
972               &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf );
973     msi_free( comp );
974     return r;
975 }
976
977 /*************************************************************************
978  *  MsiEnumComponentQualifiersW [MSI.@]
979  */
980 UINT WINAPI MsiEnumComponentQualifiersW( LPCWSTR szComponent, DWORD iIndex,
981                 LPWSTR lpQualifierBuf, DWORD* pcchQualifierBuf,
982                 LPWSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf )
983 {
984     awstring qual, appdata;
985
986     TRACE("%s %08x %p %p %p %p\n", debugstr_w(szComponent), iIndex,
987           lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf,
988           pcchApplicationDataBuf);
989
990     qual.unicode = TRUE;
991     qual.str.w = lpQualifierBuf;
992
993     appdata.unicode = TRUE;
994     appdata.str.w = lpApplicationDataBuf;
995
996     return MSI_EnumComponentQualifiers( szComponent, iIndex,
997                  &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf );
998 }
999
1000 /*************************************************************************
1001  *  MsiEnumRelatedProductsW   [MSI.@]
1002  *
1003  */
1004 UINT WINAPI MsiEnumRelatedProductsW(LPCWSTR szUpgradeCode, DWORD dwReserved,
1005                                     DWORD iProductIndex, LPWSTR lpProductBuf)
1006 {
1007     UINT r;
1008     HKEY hkey;
1009     DWORD dwSize = SQUISH_GUID_SIZE;
1010     WCHAR szKeyName[SQUISH_GUID_SIZE];
1011
1012     TRACE("%s %u %u %p\n", debugstr_w(szUpgradeCode), dwReserved,
1013           iProductIndex, lpProductBuf);
1014
1015     if (NULL == szUpgradeCode)
1016         return ERROR_INVALID_PARAMETER;
1017     if (NULL == lpProductBuf)
1018         return ERROR_INVALID_PARAMETER;
1019
1020     r = MSIREG_OpenUpgradeCodesKey(szUpgradeCode, &hkey, FALSE);
1021     if (r != ERROR_SUCCESS)
1022         return ERROR_NO_MORE_ITEMS;
1023
1024     r = RegEnumValueW(hkey, iProductIndex, szKeyName, &dwSize, NULL, NULL, NULL, NULL);
1025     if( r == ERROR_SUCCESS )
1026         unsquash_guid(szKeyName, lpProductBuf);
1027     RegCloseKey(hkey);
1028
1029     return r;
1030 }
1031
1032 /*************************************************************************
1033  *  MsiEnumRelatedProductsA   [MSI.@]
1034  *
1035  */
1036 UINT WINAPI MsiEnumRelatedProductsA(LPCSTR szUpgradeCode, DWORD dwReserved,
1037                                     DWORD iProductIndex, LPSTR lpProductBuf)
1038 {
1039     LPWSTR szwUpgradeCode = NULL;
1040     WCHAR productW[GUID_SIZE];
1041     UINT r;
1042
1043     TRACE("%s %u %u %p\n", debugstr_a(szUpgradeCode), dwReserved,
1044           iProductIndex, lpProductBuf);
1045
1046     if (szUpgradeCode)
1047     {
1048         szwUpgradeCode = strdupAtoW( szUpgradeCode );
1049         if( !szwUpgradeCode )
1050             return ERROR_OUTOFMEMORY;
1051     }
1052
1053     r = MsiEnumRelatedProductsW( szwUpgradeCode, dwReserved,
1054                                  iProductIndex, productW );
1055     if (r == ERROR_SUCCESS)
1056     {
1057         WideCharToMultiByte( CP_ACP, 0, productW, GUID_SIZE,
1058                              lpProductBuf, GUID_SIZE, NULL, NULL );
1059     }
1060     msi_free( szwUpgradeCode);
1061     return r;
1062 }
1063
1064 /***********************************************************************
1065  * MsiEnumPatchesA            [MSI.@]
1066  */
1067 UINT WINAPI MsiEnumPatchesA( LPCSTR szProduct, DWORD iPatchIndex,
1068         LPSTR lpPatchBuf, LPSTR lpTransformsBuf, DWORD* pcchTransformsBuf)
1069 {
1070     FIXME("%s %d %p %p %p\n", debugstr_a(szProduct),
1071           iPatchIndex, lpPatchBuf, lpTransformsBuf, pcchTransformsBuf);
1072     return ERROR_NO_MORE_ITEMS;
1073 }
1074
1075 /***********************************************************************
1076  * MsiEnumPatchesW            [MSI.@]
1077  */
1078 UINT WINAPI MsiEnumPatchesW( LPCWSTR szProduct, DWORD iPatchIndex,
1079         LPWSTR lpPatchBuf, LPWSTR lpTransformsBuf, DWORD* pcchTransformsBuf)
1080 {
1081     FIXME("%s %d %p %p %p\n", debugstr_w(szProduct),
1082           iPatchIndex, lpPatchBuf, lpTransformsBuf, pcchTransformsBuf);
1083     return ERROR_NO_MORE_ITEMS;
1084 }
1085
1086 UINT WINAPI MsiEnumProductsExA( LPCSTR szProductCode, LPCSTR szUserSid,
1087         DWORD dwContext, DWORD dwIndex, LPSTR szInstalledProductCode,
1088         MSIINSTALLCONTEXT* pdwInstalledContext, LPSTR szSid, LPDWORD pcchSid)
1089 {
1090     FIXME("%s %s %d %d %p %p %p %p\n", debugstr_a(szProductCode), debugstr_a(szUserSid),
1091           dwContext, dwIndex, szInstalledProductCode, pdwInstalledContext,
1092           szSid, pcchSid);
1093     return ERROR_NO_MORE_ITEMS;
1094 }
1095
1096 UINT WINAPI MsiEnumProductsExW( LPCWSTR szProductCode, LPCWSTR szUserSid,
1097         DWORD dwContext, DWORD dwIndex, LPWSTR szInstalledProductCode,
1098         MSIINSTALLCONTEXT* pdwInstalledContext, LPWSTR szSid, LPDWORD pcchSid)
1099 {
1100     FIXME("%s %s %d %d %p %p %p %p\n", debugstr_w(szProductCode), debugstr_w(szUserSid),
1101           dwContext, dwIndex, szInstalledProductCode, pdwInstalledContext,
1102           szSid, pcchSid);
1103     return ERROR_NO_MORE_ITEMS;
1104 }