msi: automation: Database::SummaryInformation is a propget, not a method.
[wine] / dlls / msi / source.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2005 Aric Stewart for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winreg.h"
29 #include "winnls.h"
30 #include "shlwapi.h"
31 #include "wine/debug.h"
32 #include "msi.h"
33 #include "msiquery.h"
34 #include "msipriv.h"
35 #include "wincrypt.h"
36 #include "winver.h"
37 #include "winuser.h"
38 #include "wine/unicode.h"
39 #include "sddl.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(msi);
42
43 /*
44  * These apis are defined in MSI 3.0
45  */
46
47 typedef struct tagMediaInfo
48 {
49     LPWSTR  path;
50     WCHAR   szIndex[10];
51     WCHAR   type;
52 } media_info;
53
54 static UINT OpenSourceKey(LPCWSTR szProduct, HKEY* key, BOOL user, BOOL create)
55 {
56     HKEY rootkey = 0; 
57     UINT rc; 
58     static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
59
60     if (user)
61         rc = MSIREG_OpenUserProductsKey(szProduct, &rootkey, create);
62     else
63         rc = MSIREG_OpenProductsKey(szProduct, &rootkey, create);
64
65     if (rc)
66         return rc;
67
68     if (create)
69         rc = RegCreateKeyW(rootkey, szSourceList, key);
70     else
71         rc = RegOpenKeyW(rootkey,szSourceList, key); 
72
73     return rc;
74 }
75
76 static UINT OpenMediaSubkey(HKEY rootkey, HKEY *key, BOOL create)
77 {
78     UINT rc;
79     static const WCHAR media[] = {'M','e','d','i','a',0};
80
81     if (create)
82         rc = RegCreateKeyW(rootkey, media, key);
83     else
84         rc = RegOpenKeyW(rootkey,media, key); 
85
86     return rc;
87 }
88
89 static UINT OpenNetworkSubkey(HKEY rootkey, HKEY *key, BOOL create)
90 {
91     UINT rc;
92     static const WCHAR net[] = {'N','e','t',0};
93
94     if (create)
95         rc = RegCreateKeyW(rootkey, net, key);
96     else
97         rc = RegOpenKeyW(rootkey, net, key); 
98
99     return rc;
100 }
101
102 static UINT OpenURLSubkey(HKEY rootkey, HKEY *key, BOOL create)
103 {
104     UINT rc;
105     static const WCHAR URL[] = {'U','R','L',0};
106
107     if (create)
108         rc = RegCreateKeyW(rootkey, URL, key);
109     else
110         rc = RegOpenKeyW(rootkey, URL, key); 
111
112     return rc;
113 }
114
115
116 static UINT find_given_source(HKEY key, LPCWSTR szSource, media_info *ss)
117 {
118     DWORD index = 0;
119     WCHAR szIndex[10];
120     DWORD size;
121     DWORD val_size;
122     LPWSTR val;
123     UINT rc = ERROR_SUCCESS;
124
125     while (rc == ERROR_SUCCESS)
126     {
127         val = NULL;
128         val_size = 0;
129         size = sizeof(szIndex)/sizeof(szIndex[0]);
130         rc = RegEnumValueW(key, index, szIndex, &size, NULL, NULL, NULL, &val_size);
131         if (rc != ERROR_NO_MORE_ITEMS)
132         {
133             val = msi_alloc(val_size);
134             RegEnumValueW(key, index, szIndex, &size, NULL, NULL, (LPBYTE)val, 
135                 &val_size);
136             if (lstrcmpiW(szSource,val)==0)
137             {
138                 ss->path = val;
139                 strcpyW(ss->szIndex,szIndex);
140                 break;
141             }
142             else
143                 strcpyW(ss->szIndex,szIndex);
144
145             msi_free(val);
146             index ++;
147         }
148     }
149     return rc;
150 }
151
152 /******************************************************************
153  *  MsiSourceListGetInfoW   (MSI.@)
154  */
155 UINT WINAPI MsiSourceListGetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
156                                    MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
157                                    LPCWSTR szProperty, LPWSTR szValue, 
158                                    LPDWORD pcchValue) 
159 {
160     HKEY sourcekey;
161     UINT rc;
162
163     TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szProperty));
164
165     if (!szProduct || lstrlenW(szProduct) > 39)
166         return ERROR_INVALID_PARAMETER;
167
168     if (szValue && !pcchValue)
169         return ERROR_INVALID_PARAMETER;
170     
171     if (dwOptions == MSICODE_PATCH)
172     {
173         FIXME("Unhandled options MSICODE_PATCH\n");
174         return ERROR_FUNCTION_FAILED;
175     }
176     
177     if (szUserSid)
178         FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
179
180     if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
181         FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
182
183     if (dwContext == MSIINSTALLCONTEXT_MACHINE)
184         rc = OpenSourceKey(szProduct, &sourcekey, FALSE, FALSE);
185     else
186         rc = OpenSourceKey(szProduct, &sourcekey, TRUE, FALSE);
187
188     if (rc != ERROR_SUCCESS)
189         return ERROR_UNKNOWN_PRODUCT;
190
191     if (strcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) == 0)
192     {
193         HKEY key;
194         rc = OpenMediaSubkey(sourcekey, &key, FALSE);
195         if (rc == ERROR_SUCCESS)
196             rc = RegQueryValueExW(key, INSTALLPROPERTY_MEDIAPACKAGEPATHW,
197                     0, 0, (LPBYTE)szValue, pcchValue);
198         if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
199             rc = ERROR_UNKNOWN_PROPERTY;
200         RegCloseKey(key);
201     }
202     else if (strcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW) ==0)
203     {
204         HKEY key;
205         rc = OpenMediaSubkey(sourcekey, &key, FALSE);
206         if (rc == ERROR_SUCCESS)
207             rc = RegQueryValueExW(key, INSTALLPROPERTY_DISKPROMPTW, 0, 0,
208                     (LPBYTE)szValue, pcchValue);
209         if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
210             rc = ERROR_UNKNOWN_PROPERTY;
211         RegCloseKey(key);
212     }
213     else if (strcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW)==0)
214     {
215         LPWSTR buffer;
216         DWORD size = 0;
217
218         RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0,
219                 NULL, &size);
220         if (size == 0)
221             rc = ERROR_UNKNOWN_PROPERTY;
222         else
223         {
224             LPWSTR ptr;
225             buffer = msi_alloc(size);
226             rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
227                     0, 0, (LPBYTE)buffer,&size); 
228             ptr = strchrW(buffer,';');
229             if (ptr) ptr = strchrW(ptr+1,';');
230             if (!ptr)
231                 rc = ERROR_UNKNOWN_PROPERTY;
232             else
233             {
234                 ptr ++;
235                 lstrcpynW(szValue, ptr, *pcchValue);
236                 if (lstrlenW(ptr) > *pcchValue)
237                 {
238                     *pcchValue = lstrlenW(ptr)+1;
239                     rc = ERROR_MORE_DATA;
240                 }
241                 else
242                     rc = ERROR_SUCCESS;
243             }
244             msi_free(buffer);
245         }
246     }
247     else if (strcmpW(INSTALLPROPERTY_LASTUSEDTYPEW, szProperty)==0)
248     {
249         LPWSTR buffer;
250         DWORD size = 0;
251
252         RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0,
253                 NULL, &size);
254         if (size == 0)
255             rc = ERROR_UNKNOWN_PROPERTY;
256         else
257         {
258             buffer = msi_alloc(size);
259             rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
260                     0, 0, (LPBYTE)buffer,&size); 
261             if (*pcchValue < 1)
262             {
263                 rc = ERROR_MORE_DATA;
264                 *pcchValue = 1;
265             }
266             else
267             {
268                 szValue[0] = buffer[0];
269                 rc = ERROR_SUCCESS;
270             }
271             msi_free(buffer);
272         }
273     }
274     else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0)
275     {
276         rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, 0, 
277                 (LPBYTE)szValue, pcchValue);
278         if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
279             rc = ERROR_UNKNOWN_PROPERTY;
280     }
281     else
282     {
283         FIXME("Unknown property %s\n",debugstr_w(szProperty));
284         rc = ERROR_UNKNOWN_PROPERTY;
285     }
286
287     RegCloseKey(sourcekey);
288     return rc;
289 }
290
291 /******************************************************************
292  *  MsiSourceListSetInfoW   (MSI.@)
293  */
294 UINT WINAPI MsiSourceListSetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
295                                    MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
296                                    LPCWSTR szProperty, LPCWSTR szValue)
297 {
298     HKEY sourcekey;
299     UINT rc;
300
301     TRACE("%s %s %x %x %s %s\n", debugstr_w(szProduct), debugstr_w(szUserSid),
302             dwContext, dwOptions, debugstr_w(szProperty), debugstr_w(szValue));
303
304     if (!szProduct || lstrlenW(szProduct) > 39)
305         return ERROR_INVALID_PARAMETER;
306
307     if (dwOptions & MSICODE_PATCH)
308     {
309         FIXME("Unhandled options MSICODE_PATCH\n");
310         return ERROR_FUNCTION_FAILED;
311     }
312     
313     if (szUserSid)
314         FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
315
316     if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
317         FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
318
319     if (dwContext == MSIINSTALLCONTEXT_MACHINE)
320         rc = OpenSourceKey(szProduct, &sourcekey, FALSE, TRUE);
321     else
322         rc = OpenSourceKey(szProduct, &sourcekey, TRUE, TRUE);
323
324     if (rc != ERROR_SUCCESS)
325         return ERROR_UNKNOWN_PRODUCT;
326
327
328     if (strcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) == 0)
329     {
330         HKEY key;
331         DWORD size = lstrlenW(szValue)*sizeof(WCHAR);
332         rc = OpenMediaSubkey(sourcekey, &key, FALSE);
333         if (rc == ERROR_SUCCESS)
334             rc = RegSetValueExW(key, INSTALLPROPERTY_MEDIAPACKAGEPATHW, 0,
335                     REG_SZ, (const BYTE *)szValue, size);
336         if (rc != ERROR_SUCCESS)
337             rc = ERROR_UNKNOWN_PROPERTY;
338         RegCloseKey(key);
339     }
340     else if (strcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW) == 0)
341     {
342         HKEY key;
343         DWORD size = lstrlenW(szValue)*sizeof(WCHAR);
344         rc = OpenMediaSubkey(sourcekey, &key, FALSE);
345         if (rc == ERROR_SUCCESS)
346             rc = RegSetValueExW(key, INSTALLPROPERTY_DISKPROMPTW, 0,
347                     REG_SZ, (const BYTE *)szValue, size);
348         if (rc != ERROR_SUCCESS)
349             rc = ERROR_UNKNOWN_PROPERTY;
350         RegCloseKey(key);
351     }
352     else if (strcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW)==0)
353     {
354         LPWSTR buffer = NULL;
355         DWORD size;
356         WCHAR typechar = 'n';
357         static const WCHAR LastUsedSource_Fmt[] = {'%','c',';','%','i',';','%','s',0};
358
359         /* make sure the source is registered */
360         MsiSourceListAddSourceExW(szProduct, szUserSid, dwContext, 
361                 dwOptions, szValue, 0); 
362
363         if (dwOptions & MSISOURCETYPE_NETWORK)
364             typechar = 'n';
365         else if (dwOptions & MSISOURCETYPE_URL)
366             typechar = 'u';
367         else if (dwOptions & MSISOURCETYPE_MEDIA)
368             typechar = 'm';
369         else
370             ERR("Unknown source type! %x\n", dwOptions);
371
372         size = (lstrlenW(szValue)+5)*sizeof(WCHAR);
373         buffer = msi_alloc(size);
374         sprintfW(buffer, LastUsedSource_Fmt, typechar, 1, szValue);
375         rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 
376                 REG_EXPAND_SZ, (LPBYTE)buffer, size);
377         if (rc != ERROR_SUCCESS)
378             rc = ERROR_UNKNOWN_PROPERTY;
379         msi_free( buffer );
380     }
381     else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0)
382     {
383         DWORD size = lstrlenW(szValue)*sizeof(WCHAR);
384         rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0,
385                 REG_SZ, (const BYTE *)szValue, size);
386         if (rc != ERROR_SUCCESS)
387             rc = ERROR_UNKNOWN_PROPERTY;
388     }
389     else
390     {
391         FIXME("Unknown property %s\n",debugstr_w(szProperty));
392         rc = ERROR_UNKNOWN_PROPERTY;
393     }
394
395     RegCloseKey(sourcekey);
396     return rc;
397
398 }
399
400 /******************************************************************
401  *  MsiSourceListAddSourceW (MSI.@)
402  */
403 UINT WINAPI MsiSourceListAddSourceW( LPCWSTR szProduct, LPCWSTR szUserName,
404         DWORD dwReserved, LPCWSTR szSource)
405 {
406     INT ret;
407     LPWSTR sidstr = NULL;
408     DWORD sidsize = 0;
409     DWORD domsize = 0;
410
411     TRACE("%s %s %s\n", debugstr_w(szProduct), debugstr_w(szUserName), debugstr_w(szSource));
412
413     if (LookupAccountNameW(NULL, szUserName, NULL, &sidsize, NULL, &domsize, NULL))
414     {
415         PSID psid = msi_alloc(sidsize);
416
417         if (LookupAccountNameW(NULL, szUserName, psid, &sidsize, NULL, &domsize, NULL))
418             ConvertSidToStringSidW(psid, &sidstr);
419
420         msi_free(psid);
421     }
422
423     ret = MsiSourceListAddSourceExW(szProduct, sidstr, 
424         MSIINSTALLCONTEXT_USERMANAGED, MSISOURCETYPE_NETWORK, szSource, 0);
425
426     if (sidstr)
427         LocalFree(sidstr);
428
429     return ret;
430 }
431
432 /******************************************************************
433  *  MsiSourceListAddSourceA (MSI.@)
434  */
435 UINT WINAPI MsiSourceListAddSourceA( LPCSTR szProduct, LPCSTR szUserName,
436         DWORD dwReserved, LPCSTR szSource)
437 {
438     INT ret;
439     LPWSTR szwproduct;
440     LPWSTR szwusername;
441     LPWSTR szwsource;
442
443     szwproduct = strdupAtoW( szProduct );
444     szwusername = strdupAtoW( szUserName );
445     szwsource = strdupAtoW( szSource );
446
447     ret = MsiSourceListAddSourceW(szwproduct, szwusername, 0, szwsource);
448
449     msi_free(szwproduct);
450     msi_free(szwusername);
451     msi_free(szwsource);
452
453     return ret;
454 }
455
456 /******************************************************************
457  *  MsiSourceListAddSourceExW (MSI.@)
458  */
459 UINT WINAPI MsiSourceListAddSourceExW( LPCWSTR szProduct, LPCWSTR szUserSid,
460         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szSource, 
461         DWORD dwIndex)
462 {
463     HKEY sourcekey;
464     HKEY typekey;
465     UINT rc;
466     media_info source_struct;
467
468     TRACE("%s %s %x %x %s %i\n", debugstr_w(szProduct), debugstr_w(szUserSid),
469           dwContext, dwOptions, debugstr_w(szSource), dwIndex);
470
471     if (!szProduct)
472         return ERROR_INVALID_PARAMETER;
473
474     if (!szSource)
475         return ERROR_INVALID_PARAMETER;
476
477     if (dwOptions & MSICODE_PATCH)
478     {
479         FIXME("Unhandled options MSICODE_PATCH\n");
480         return ERROR_FUNCTION_FAILED;
481     }
482
483     if (szUserSid)
484         FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
485
486     if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
487         FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
488
489     if (dwContext == MSIINSTALLCONTEXT_MACHINE)
490         rc = OpenSourceKey(szProduct, &sourcekey, FALSE, TRUE);
491     else
492         rc = OpenSourceKey(szProduct, &sourcekey, TRUE, TRUE);
493
494     if (rc != ERROR_SUCCESS)
495         return ERROR_UNKNOWN_PRODUCT;
496
497     if (dwOptions & MSISOURCETYPE_NETWORK)
498         rc = OpenNetworkSubkey(sourcekey, &typekey, TRUE);
499     else if (dwOptions & MSISOURCETYPE_URL)
500         rc = OpenURLSubkey(sourcekey, &typekey, TRUE);
501     else if (dwOptions & MSISOURCETYPE_MEDIA)
502         rc = OpenMediaSubkey(sourcekey, &typekey, TRUE);
503     else
504     {
505         ERR("unknown media type: %08x\n", dwOptions);
506         RegCloseKey(sourcekey);
507         return ERROR_FUNCTION_FAILED;
508     }
509
510     source_struct.szIndex[0] = 0;
511     if (find_given_source(typekey, szSource, &source_struct)==ERROR_SUCCESS)
512     {
513         DWORD current_index = atoiW(source_struct.szIndex);
514         /* found the source */
515         if (dwIndex > 0 && current_index != dwIndex)
516             FIXME("Need to reorder the sources!\n");
517         msi_free( source_struct.path );
518     }
519     else
520     {
521         DWORD current_index = 0;
522         static const WCHAR fmt[] = {'%','i',0};
523         DWORD size = lstrlenW(szSource)*sizeof(WCHAR);
524
525         if (source_struct.szIndex[0])
526             current_index = atoiW(source_struct.szIndex);
527         /* new source */
528         if (dwIndex > 0 && dwIndex < current_index)
529             FIXME("Need to reorder the sources!\n");
530
531         current_index ++;
532         sprintfW(source_struct.szIndex,fmt,current_index);
533         rc = RegSetValueExW(typekey, source_struct.szIndex, 0, REG_EXPAND_SZ, 
534                 (const BYTE *)szSource, size);
535     }
536
537     RegCloseKey(typekey);
538     RegCloseKey(sourcekey);
539     return rc;
540 }
541
542 /******************************************************************
543  *  MsiSourceListAddMediaDisk(MSI.@)
544  */
545 UINT WINAPI MsiSourceListAddMediaDiskW(LPCWSTR szProduct, LPCWSTR szUserSid, 
546         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId, 
547         LPCWSTR szVolumeLabel, LPCWSTR szDiskPrompt)
548 {
549     HKEY sourcekey;
550     HKEY mediakey;
551     UINT rc;
552     WCHAR szIndex[10];
553     static const WCHAR fmt[] = {'%','i',0};
554     static const WCHAR disk_fmt[] = {'%','s',';','%','s',0};
555     static const WCHAR empty[1] = {0};
556     LPCWSTR pt1,pt2;
557     LPWSTR buffer;
558     DWORD size;
559
560     TRACE("%s %s %x %x %i %s %s\n", debugstr_w(szProduct),
561             debugstr_w(szUserSid), dwContext, dwOptions, dwDiskId,
562             debugstr_w(szVolumeLabel), debugstr_w(szDiskPrompt));
563
564     if (!szProduct || lstrlenW(szProduct) > 39)
565         return ERROR_INVALID_PARAMETER;
566
567     if (dwOptions & MSICODE_PATCH)
568     {
569         FIXME("Unhandled options MSICODE_PATCH\n");
570         return ERROR_FUNCTION_FAILED;
571     }
572     
573     if (szUserSid)
574         FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
575
576     if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
577         FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
578
579     if (dwContext == MSIINSTALLCONTEXT_MACHINE)
580         rc = OpenSourceKey(szProduct, &sourcekey, FALSE, TRUE);
581     else
582         rc = OpenSourceKey(szProduct, &sourcekey, TRUE, TRUE);
583
584     if (rc != ERROR_SUCCESS)
585         return ERROR_UNKNOWN_PRODUCT;
586
587     OpenMediaSubkey(sourcekey,&mediakey,TRUE);
588
589     sprintfW(szIndex,fmt,dwDiskId);
590
591     size = 2;
592     if (szVolumeLabel)
593     {
594         size +=lstrlenW(szVolumeLabel);
595         pt1 = szVolumeLabel;
596     }
597     else
598         pt1 = empty;
599     if (szDiskPrompt)
600     {
601         size +=lstrlenW(szDiskPrompt);
602         pt2 = szDiskPrompt;
603     }
604     else
605         pt2 = empty;
606
607     size *=sizeof(WCHAR);
608
609     buffer = msi_alloc(size);
610     sprintfW(buffer,disk_fmt,pt1,pt2);
611
612     RegSetValueExW(mediakey, szIndex, 0, REG_SZ, (LPBYTE)buffer, size);
613     msi_free( buffer );
614
615     RegCloseKey(sourcekey);
616     RegCloseKey(mediakey);
617
618     return ERROR_SUCCESS;
619 }
620
621 /******************************************************************
622  *  MsiSourceListAddSourceExA (MSI.@)
623  */
624 UINT WINAPI MsiSourceListClearAllA( LPCSTR szProduct, LPCSTR szUserName, DWORD dwReserved )
625 {
626     FIXME("(%s %s %d)\n", debugstr_a(szProduct), debugstr_a(szUserName), dwReserved);
627     return ERROR_SUCCESS;
628 }
629
630 /******************************************************************
631  *  MsiSourceListAddSourceExW (MSI.@)
632  */
633 UINT WINAPI MsiSourceListClearAllW( LPCWSTR szProduct, LPCWSTR szUserName, DWORD dwReserved )
634 {
635     FIXME("(%s %s %d)\n", debugstr_w(szProduct), debugstr_w(szUserName), dwReserved);
636     return ERROR_SUCCESS;
637 }