msi: Add tests for registering and unregistering class info.
[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     struct list entry;
50     LPWSTR  path;
51     WCHAR   szIndex[10];
52     DWORD   index;
53 } media_info;
54
55 static UINT OpenSourceKey(LPCWSTR szProduct, HKEY* key, DWORD dwOptions,
56                           MSIINSTALLCONTEXT context, BOOL create)
57 {
58     HKEY rootkey = 0; 
59     UINT rc = ERROR_FUNCTION_FAILED;
60
61     if (context == MSIINSTALLCONTEXT_USERUNMANAGED)
62     {
63         if (dwOptions & MSICODE_PATCH)
64             rc = MSIREG_OpenUserPatchesKey(szProduct, &rootkey, create);
65         else
66             rc = MSIREG_OpenProductKey(szProduct, NULL, context,
67                                        &rootkey, create);
68     }
69     else if (context == MSIINSTALLCONTEXT_USERMANAGED)
70     {
71         if (dwOptions & MSICODE_PATCH)
72             rc = MSIREG_OpenUserPatchesKey(szProduct, &rootkey, create);
73         else
74             rc = MSIREG_OpenProductKey(szProduct, NULL, context,
75                                        &rootkey, create);
76     }
77     else if (context == MSIINSTALLCONTEXT_MACHINE)
78     {
79         if (dwOptions & MSICODE_PATCH)
80             rc = MSIREG_OpenPatchesKey(szProduct, &rootkey, create);
81         else
82             rc = MSIREG_OpenProductKey(szProduct, NULL, context,
83                                        &rootkey, create);
84     }
85
86     if (rc != ERROR_SUCCESS)
87     {
88         if (dwOptions & MSICODE_PATCH)
89             return ERROR_UNKNOWN_PATCH;
90         else
91             return ERROR_UNKNOWN_PRODUCT;
92     }
93
94     if (create)
95         rc = RegCreateKeyW(rootkey, szSourceList, key);
96     else
97     {
98         rc = RegOpenKeyW(rootkey,szSourceList, key);
99         if (rc != ERROR_SUCCESS)
100             rc = ERROR_BAD_CONFIGURATION;
101     }
102
103     return rc;
104 }
105
106 static UINT OpenMediaSubkey(HKEY rootkey, HKEY *key, BOOL create)
107 {
108     UINT rc;
109     static const WCHAR media[] = {'M','e','d','i','a',0};
110
111     if (create)
112         rc = RegCreateKeyW(rootkey, media, key);
113     else
114         rc = RegOpenKeyW(rootkey,media, key); 
115
116     return rc;
117 }
118
119 static UINT OpenNetworkSubkey(HKEY rootkey, HKEY *key, BOOL create)
120 {
121     UINT rc;
122     static const WCHAR net[] = {'N','e','t',0};
123
124     if (create)
125         rc = RegCreateKeyW(rootkey, net, key);
126     else
127         rc = RegOpenKeyW(rootkey, net, key); 
128
129     return rc;
130 }
131
132 static UINT OpenURLSubkey(HKEY rootkey, HKEY *key, BOOL create)
133 {
134     UINT rc;
135     static const WCHAR URL[] = {'U','R','L',0};
136
137     if (create)
138         rc = RegCreateKeyW(rootkey, URL, key);
139     else
140         rc = RegOpenKeyW(rootkey, URL, key); 
141
142     return rc;
143 }
144
145 /******************************************************************
146  *  MsiSourceListEnumMediaDisksA   (MSI.@)
147  */
148 UINT WINAPI MsiSourceListEnumMediaDisksA(LPCSTR szProductCodeOrPatchCode,
149                                          LPCSTR szUserSid, MSIINSTALLCONTEXT dwContext,
150                                          DWORD dwOptions, DWORD dwIndex, LPDWORD pdwDiskId,
151                                          LPSTR szVolumeLabel, LPDWORD pcchVolumeLabel,
152                                          LPSTR szDiskPrompt, LPDWORD pcchDiskPrompt)
153 {
154     LPWSTR product = NULL;
155     LPWSTR usersid = NULL;
156     LPWSTR volume = NULL;
157     LPWSTR prompt = NULL;
158     UINT r = ERROR_INVALID_PARAMETER;
159
160     TRACE("(%s, %s, %d, %d, %d, %p, %p, %p, %p, %p)\n", debugstr_a(szProductCodeOrPatchCode),
161           debugstr_a(szUserSid), dwContext, dwOptions, dwIndex, pdwDiskId,
162           szVolumeLabel, pcchVolumeLabel, szDiskPrompt, pcchDiskPrompt);
163
164     if (szDiskPrompt && !pcchDiskPrompt)
165         return ERROR_INVALID_PARAMETER;
166
167     if (szProductCodeOrPatchCode) product = strdupAtoW(szProductCodeOrPatchCode);
168     if (szUserSid) usersid = strdupAtoW(szUserSid);
169
170     /* FIXME: add tests for an invalid format */
171
172     if (pcchVolumeLabel)
173         volume = msi_alloc(*pcchVolumeLabel * sizeof(WCHAR));
174
175     if (pcchDiskPrompt)
176         prompt = msi_alloc(*pcchDiskPrompt * sizeof(WCHAR));
177
178     if (volume) *volume = '\0';
179     if (prompt) *prompt = '\0';
180     r = MsiSourceListEnumMediaDisksW(product, usersid, dwContext, dwOptions,
181                                      dwIndex, pdwDiskId, volume, pcchVolumeLabel,
182                                      prompt, pcchDiskPrompt);
183     if (r != ERROR_SUCCESS)
184         goto done;
185
186     if (szVolumeLabel && pcchVolumeLabel)
187         WideCharToMultiByte(CP_ACP, 0, volume, -1, szVolumeLabel,
188                             *pcchVolumeLabel + 1, NULL, NULL);
189
190     if (szDiskPrompt)
191         WideCharToMultiByte(CP_ACP, 0, prompt, -1, szDiskPrompt,
192                             *pcchDiskPrompt + 1, NULL, NULL);
193
194 done:
195     msi_free(product);
196     msi_free(usersid);
197     msi_free(volume);
198     msi_free(prompt);
199
200     return r;
201 }
202
203 /******************************************************************
204  *  MsiSourceListEnumMediaDisksW   (MSI.@)
205  */
206 UINT WINAPI MsiSourceListEnumMediaDisksW(LPCWSTR szProductCodeOrPatchCode,
207                                          LPCWSTR szUserSid, MSIINSTALLCONTEXT dwContext,
208                                          DWORD dwOptions, DWORD dwIndex, LPDWORD pdwDiskId,
209                                          LPWSTR szVolumeLabel, LPDWORD pcchVolumeLabel,
210                                          LPWSTR szDiskPrompt, LPDWORD pcchDiskPrompt)
211 {
212     WCHAR squished_pc[GUID_SIZE];
213     WCHAR convert[11];
214     LPWSTR value = NULL;
215     LPWSTR data = NULL;
216     LPWSTR ptr, ptr2;
217     HKEY source, media;
218     DWORD valuesz, datasz = 0;
219     DWORD type;
220     DWORD numvals, size;
221     LONG res;
222     UINT r;
223     static DWORD index = 0;
224
225     static const WCHAR fmt[] = {'#','%','d',0};
226
227     TRACE("(%s, %s, %d, %d, %d, %p, %p, %p, %p)\n", debugstr_w(szProductCodeOrPatchCode),
228           debugstr_w(szUserSid), dwContext, dwOptions, dwIndex, szVolumeLabel,
229           pcchVolumeLabel, szDiskPrompt, pcchDiskPrompt);
230
231     if (!szProductCodeOrPatchCode ||
232         !squash_guid(szProductCodeOrPatchCode, squished_pc))
233         return ERROR_INVALID_PARAMETER;
234
235     if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid)
236         return ERROR_INVALID_PARAMETER;
237
238     if (dwOptions != MSICODE_PRODUCT && dwOptions != MSICODE_PATCH)
239         return ERROR_INVALID_PARAMETER;
240
241     if (szDiskPrompt && !pcchDiskPrompt)
242         return ERROR_INVALID_PARAMETER;
243
244     if (dwIndex == 0)
245         index = 0;
246
247     if (dwIndex != index)
248         return ERROR_INVALID_PARAMETER;
249
250     r = OpenSourceKey(szProductCodeOrPatchCode, &source,
251                       dwOptions, dwContext, FALSE);
252     if (r != ERROR_SUCCESS)
253         return r;
254
255     r = OpenMediaSubkey(source, &media, FALSE);
256     if (r != ERROR_SUCCESS)
257     {
258         RegCloseKey(source);
259         return ERROR_NO_MORE_ITEMS;
260     }
261
262     if (!pcchVolumeLabel && !pcchDiskPrompt)
263     {
264         r = RegEnumValueW(media, dwIndex, NULL, NULL, NULL,
265                           &type, NULL, NULL);
266         goto done;
267     }
268
269     res = RegQueryInfoKeyW(media, NULL, NULL, NULL, NULL, NULL,
270                            NULL, &numvals, &valuesz, &datasz, NULL, NULL);
271     if (res != ERROR_SUCCESS)
272     {
273         r = ERROR_BAD_CONFIGURATION;
274         goto done;
275     }
276
277     value = msi_alloc(++valuesz * sizeof(WCHAR));
278     data = msi_alloc(++datasz * sizeof(WCHAR));
279     if (!value || !data)
280     {
281         r = ERROR_OUTOFMEMORY;
282         goto done;
283     }
284
285     r = RegEnumValueW(media, dwIndex, value, &valuesz,
286                       NULL, &type, (LPBYTE)data, &datasz);
287     if (r != ERROR_SUCCESS)
288         goto done;
289
290     if (pdwDiskId)
291         *pdwDiskId = atolW(value);
292
293     ptr2 = data;
294     ptr = strchrW(data, ';');
295     if (!ptr)
296         ptr = data;
297     else
298         *ptr = '\0';
299
300     if (pcchVolumeLabel)
301     {
302         if (type == REG_DWORD)
303         {
304             sprintfW(convert, fmt, *data);
305             size = lstrlenW(convert);
306             ptr2 = convert;
307         }
308         else
309             size = lstrlenW(data);
310
311         if (size >= *pcchVolumeLabel)
312             r = ERROR_MORE_DATA;
313         else if (szVolumeLabel)
314             lstrcpyW(szVolumeLabel, ptr2);
315
316         *pcchVolumeLabel = size;
317     }
318
319     if (pcchDiskPrompt)
320     {
321         if (!*ptr)
322             ptr++;
323
324         if (type == REG_DWORD)
325         {
326             sprintfW(convert, fmt, *ptr);
327             size = lstrlenW(convert);
328             ptr = convert;
329         }
330         else
331             size = lstrlenW(ptr);
332
333         size = lstrlenW(ptr);
334         if (size >= *pcchDiskPrompt)
335             r = ERROR_MORE_DATA;
336         else if (szDiskPrompt)
337             lstrcpyW(szDiskPrompt, ptr);
338
339         *pcchDiskPrompt = size;
340     }
341
342     index++;
343
344 done:
345     msi_free(value);
346     msi_free(data);
347     RegCloseKey(source);
348
349     return r;
350 }
351
352 /******************************************************************
353  *  MsiSourceListEnumSourcesA   (MSI.@)
354  */
355 UINT WINAPI MsiSourceListEnumSourcesA(LPCSTR szProductCodeOrPatch, LPCSTR szUserSid,
356                                       MSIINSTALLCONTEXT dwContext,
357                                       DWORD dwOptions, DWORD dwIndex,
358                                       LPSTR szSource, LPDWORD pcchSource)
359 {
360     LPWSTR product = NULL;
361     LPWSTR usersid = NULL;
362     LPWSTR source = NULL;
363     DWORD len = 0;
364     UINT r = ERROR_INVALID_PARAMETER;
365     static DWORD index = 0;
366
367     TRACE("(%s, %s, %d, %d, %d, %p, %p)\n", debugstr_a(szProductCodeOrPatch),
368           debugstr_a(szUserSid), dwContext, dwOptions, dwIndex, szSource, pcchSource);
369
370     if (dwIndex == 0)
371         index = 0;
372
373     if (szSource && !pcchSource)
374         goto done;
375
376     if (dwIndex != index)
377         goto done;
378
379     if (szProductCodeOrPatch) product = strdupAtoW(szProductCodeOrPatch);
380     if (szUserSid) usersid = strdupAtoW(szUserSid);
381
382     r = MsiSourceListEnumSourcesW(product, usersid, dwContext, dwOptions,
383                                   dwIndex, NULL, &len);
384     if (r != ERROR_SUCCESS)
385         goto done;
386
387     source = msi_alloc(++len * sizeof(WCHAR));
388     if (!source)
389     {
390         r = ERROR_OUTOFMEMORY;
391         goto done;
392     }
393
394     *source = '\0';
395     r = MsiSourceListEnumSourcesW(product, usersid, dwContext, dwOptions,
396                                   dwIndex, source, &len);
397     if (r != ERROR_SUCCESS)
398         goto done;
399
400     len = WideCharToMultiByte(CP_ACP, 0, source, -1, NULL, 0, NULL, NULL);
401     if (pcchSource && *pcchSource >= len)
402         WideCharToMultiByte(CP_ACP, 0, source, -1, szSource, len, NULL, NULL);
403     else if (szSource)
404         r = ERROR_MORE_DATA;
405
406     if (pcchSource)
407         *pcchSource = len - 1;
408
409 done:
410     msi_free(product);
411     msi_free(usersid);
412     msi_free(source);
413
414     if (r == ERROR_SUCCESS)
415     {
416         if (szSource || !pcchSource) index++;
417     }
418     else if (dwIndex > index)
419         index = 0;
420
421     return r;
422 }
423
424 /******************************************************************
425  *  MsiSourceListEnumSourcesW   (MSI.@)
426  */
427 UINT WINAPI MsiSourceListEnumSourcesW(LPCWSTR szProductCodeOrPatch, LPCWSTR szUserSid,
428                                       MSIINSTALLCONTEXT dwContext,
429                                       DWORD dwOptions, DWORD dwIndex,
430                                       LPWSTR szSource, LPDWORD pcchSource)
431 {
432     WCHAR squished_pc[GUID_SIZE];
433     WCHAR name[32];
434     HKEY source = NULL;
435     HKEY subkey = NULL;
436     LONG res;
437     UINT r = ERROR_INVALID_PARAMETER;
438     static DWORD index = 0;
439
440     static const WCHAR format[] = {'%','d',0};
441
442     TRACE("(%s, %s, %d, %d, %d, %p, %p)\n", debugstr_w(szProductCodeOrPatch),
443           debugstr_w(szUserSid), dwContext, dwOptions, dwIndex, szSource, pcchSource);
444
445     if (dwIndex == 0)
446         index = 0;
447
448     if (!szProductCodeOrPatch || !squash_guid(szProductCodeOrPatch, squished_pc))
449         goto done;
450
451     if (szSource && !pcchSource)
452         goto done;
453
454     if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
455         goto done;
456
457     if ((dwOptions & MSISOURCETYPE_NETWORK) && (dwOptions & MSISOURCETYPE_URL))
458         goto done;
459
460     if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid)
461         goto done;
462
463     if (dwIndex != index)
464         goto done;
465
466     r = OpenSourceKey(szProductCodeOrPatch, &source,
467                       dwOptions, dwContext, FALSE);
468     if (r != ERROR_SUCCESS)
469         goto done;
470
471     if (dwOptions & MSISOURCETYPE_NETWORK)
472         r = OpenNetworkSubkey(source, &subkey, FALSE);
473     else if (dwOptions & MSISOURCETYPE_URL)
474         r = OpenURLSubkey(source, &subkey, FALSE);
475
476     if (r != ERROR_SUCCESS)
477     {
478         r = ERROR_NO_MORE_ITEMS;
479         goto done;
480     }
481
482     sprintfW(name, format, dwIndex + 1);
483
484     res = RegQueryValueExW(subkey, name, 0, 0, (LPBYTE)szSource, pcchSource);
485     if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA)
486         r = ERROR_NO_MORE_ITEMS;
487
488 done:
489     RegCloseKey(subkey);
490     RegCloseKey(source);
491
492     if (r == ERROR_SUCCESS)
493     {
494         if (szSource || !pcchSource) index++;
495     }
496     else if (dwIndex > index)
497         index = 0;
498
499     return r;
500 }
501
502 /******************************************************************
503  *  MsiSourceListGetInfoA   (MSI.@)
504  */
505 UINT WINAPI MsiSourceListGetInfoA( LPCSTR szProduct, LPCSTR szUserSid,
506                                    MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
507                                    LPCSTR szProperty, LPSTR szValue,
508                                    LPDWORD pcchValue)
509 {
510     UINT ret;
511     LPWSTR product = NULL;
512     LPWSTR usersid = NULL;
513     LPWSTR property = NULL;
514     LPWSTR value = NULL;
515     DWORD len = 0;
516
517     if (szValue && !pcchValue)
518         return ERROR_INVALID_PARAMETER;
519
520     if (szProduct) product = strdupAtoW(szProduct);
521     if (szUserSid) usersid = strdupAtoW(szUserSid);
522     if (szProperty) property = strdupAtoW(szProperty);
523
524     ret = MsiSourceListGetInfoW(product, usersid, dwContext, dwOptions,
525                                 property, NULL, &len);
526     if (ret != ERROR_SUCCESS)
527         goto done;
528
529     value = msi_alloc(++len * sizeof(WCHAR));
530     if (!value)
531         return ERROR_OUTOFMEMORY;
532
533     *value = '\0';
534     ret = MsiSourceListGetInfoW(product, usersid, dwContext, dwOptions,
535                                 property, value, &len);
536     if (ret != ERROR_SUCCESS)
537         goto done;
538
539     len = WideCharToMultiByte(CP_ACP, 0, value, -1, NULL, 0, NULL, NULL);
540     if (*pcchValue >= len)
541         WideCharToMultiByte(CP_ACP, 0, value, -1, szValue, len, NULL, NULL);
542     else if (szValue)
543         ret = ERROR_MORE_DATA;
544
545     *pcchValue = len - 1;
546
547 done:
548     msi_free(product);
549     msi_free(usersid);
550     msi_free(property);
551     msi_free(value);
552     return ret;
553 }
554
555 /******************************************************************
556  *  MsiSourceListGetInfoW   (MSI.@)
557  */
558 UINT WINAPI MsiSourceListGetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
559                                    MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
560                                    LPCWSTR szProperty, LPWSTR szValue, 
561                                    LPDWORD pcchValue) 
562 {
563     WCHAR squished_pc[GUID_SIZE];
564     HKEY sourcekey, media;
565     LPWSTR source, ptr;
566     DWORD size;
567     UINT rc;
568
569     static const WCHAR mediapack[] = {
570         'M','e','d','i','a','P','a','c','k','a','g','e',0};
571
572     TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szProperty));
573
574     if (!szProduct || !squash_guid(szProduct, squished_pc))
575         return ERROR_INVALID_PARAMETER;
576
577     if (szValue && !pcchValue)
578         return ERROR_INVALID_PARAMETER;
579
580     if (dwContext != MSIINSTALLCONTEXT_USERMANAGED &&
581         dwContext != MSIINSTALLCONTEXT_USERUNMANAGED &&
582         dwContext != MSIINSTALLCONTEXT_MACHINE)
583         return ERROR_INVALID_PARAMETER;
584
585     if (!szProperty)
586         return ERROR_INVALID_PARAMETER;
587
588     if (szUserSid)
589         FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
590
591     if (dwContext != MSIINSTALLCONTEXT_USERUNMANAGED)
592         FIXME("Unhandled context %d\n", dwContext);
593
594     rc = OpenSourceKey(szProduct, &sourcekey, dwOptions, dwContext, FALSE);
595     if (rc != ERROR_SUCCESS)
596         return rc;
597
598     if (!lstrcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) ||
599         !lstrcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW))
600     {
601         rc = OpenMediaSubkey(sourcekey, &media, FALSE);
602         if (rc != ERROR_SUCCESS)
603         {
604             RegCloseKey(sourcekey);
605             return ERROR_SUCCESS;
606         }
607
608         if (!lstrcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW))
609             szProperty = mediapack;
610
611         RegQueryValueExW(media, szProperty, 0, 0, (LPBYTE)szValue, pcchValue);
612         RegCloseKey(media);
613     }
614     else if (!lstrcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW) ||
615              !lstrcmpW(szProperty, INSTALLPROPERTY_LASTUSEDTYPEW))
616     {
617         rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
618                               0, 0, NULL, &size);
619         if (rc != ERROR_SUCCESS)
620         {
621             RegCloseKey(sourcekey);
622             return ERROR_SUCCESS;
623         }
624
625         source = msi_alloc(size);
626         RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
627                          0, 0, (LPBYTE)source, &size);
628
629         if (!*source)
630         {
631             msi_free(source);
632             RegCloseKey(sourcekey);
633             return ERROR_SUCCESS;
634         }
635
636         if (!lstrcmpW(szProperty, INSTALLPROPERTY_LASTUSEDTYPEW))
637         {
638             if (*source != 'n' && *source != 'u' && *source != 'm')
639             {
640                 msi_free(source);
641                 RegCloseKey(sourcekey);
642                 return ERROR_SUCCESS;
643             }
644
645             ptr = source;
646             source[1] = '\0';
647         }
648         else
649         {
650             ptr = strrchrW(source, ';');
651             if (!ptr)
652                 ptr = source;
653             else
654                 ptr++;
655         }
656
657         if (szValue)
658         {
659             if (strlenW(ptr) < *pcchValue)
660                 lstrcpyW(szValue, ptr);
661             else
662                 rc = ERROR_MORE_DATA;
663         }
664
665         *pcchValue = lstrlenW(ptr);
666         msi_free(source);
667     }
668     else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0)
669     {
670         *pcchValue = *pcchValue * sizeof(WCHAR);
671         rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, 0,
672                               (LPBYTE)szValue, pcchValue);
673         if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
674         {
675             *pcchValue = 0;
676             rc = ERROR_SUCCESS;
677         }
678         else
679         {
680             if (*pcchValue)
681                 *pcchValue = (*pcchValue - 1) / sizeof(WCHAR);
682             if (szValue)
683                 szValue[*pcchValue] = '\0';
684         }
685     }
686     else
687     {
688         FIXME("Unknown property %s\n",debugstr_w(szProperty));
689         rc = ERROR_UNKNOWN_PROPERTY;
690     }
691
692     RegCloseKey(sourcekey);
693     return rc;
694 }
695
696 /******************************************************************
697  *  MsiSourceListSetInfoA   (MSI.@)
698  */
699 UINT WINAPI MsiSourceListSetInfoA(LPCSTR szProduct, LPCSTR szUserSid,
700                                   MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
701                                   LPCSTR szProperty, LPCSTR szValue)
702 {
703     UINT ret;
704     LPWSTR product = NULL;
705     LPWSTR usersid = NULL;
706     LPWSTR property = NULL;
707     LPWSTR value = NULL;
708
709     if (szProduct) product = strdupAtoW(szProduct);
710     if (szUserSid) usersid = strdupAtoW(szUserSid);
711     if (szProperty) property = strdupAtoW(szProperty);
712     if (szValue) value = strdupAtoW(szValue);
713
714     ret = MsiSourceListSetInfoW(product, usersid, dwContext, dwOptions,
715                                 property, value);
716
717     msi_free(product);
718     msi_free(usersid);
719     msi_free(property);
720     msi_free(value);
721
722     return ret;
723 }
724
725 UINT msi_set_last_used_source(LPCWSTR product, LPCWSTR usersid,
726                               MSIINSTALLCONTEXT context, DWORD options,
727                               LPCWSTR value)
728 {
729     HKEY source;
730     LPWSTR buffer;
731     WCHAR typechar;
732     DWORD size;
733     UINT r;
734     int index = 1;
735
736     static const WCHAR format[] = {'%','c',';','%','i',';','%','s',0};
737
738     if (options & MSISOURCETYPE_NETWORK)
739         typechar = 'n';
740     else if (options & MSISOURCETYPE_URL)
741         typechar = 'u';
742     else if (options & MSISOURCETYPE_MEDIA)
743         typechar = 'm';
744     else
745         return ERROR_INVALID_PARAMETER;
746
747     if (!(options & MSISOURCETYPE_MEDIA))
748     {
749         r = MsiSourceListAddSourceExW(product, usersid, context,
750                                       options, value, 0);
751         if (r != ERROR_SUCCESS)
752             return r;
753
754         index = 0;
755         while ((r = MsiSourceListEnumSourcesW(product, usersid, context, options,
756                                               index, NULL, NULL)) == ERROR_SUCCESS)
757             index++;
758
759         if (r != ERROR_NO_MORE_ITEMS)
760             return r;
761     }
762
763     size = (lstrlenW(format) + lstrlenW(value) + 7) * sizeof(WCHAR);
764     buffer = msi_alloc(size);
765     if (!buffer)
766         return ERROR_OUTOFMEMORY;
767
768     r = OpenSourceKey(product, &source, MSICODE_PRODUCT, context, FALSE);
769     if (r != ERROR_SUCCESS)
770         return r;
771
772     sprintfW(buffer, format, typechar, index, value);
773
774     size = (lstrlenW(buffer) + 1) * sizeof(WCHAR);
775     r = RegSetValueExW(source, INSTALLPROPERTY_LASTUSEDSOURCEW, 0,
776                        REG_SZ, (LPBYTE)buffer, size);
777     msi_free(buffer);
778
779     RegCloseKey(source);
780     return r;
781 }
782
783 /******************************************************************
784  *  MsiSourceListSetInfoW   (MSI.@)
785  */
786 UINT WINAPI MsiSourceListSetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
787                                    MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
788                                    LPCWSTR szProperty, LPCWSTR szValue)
789 {
790     WCHAR squished_pc[GUID_SIZE];
791     HKEY sourcekey, media;
792     LPCWSTR property;
793     UINT rc;
794
795     static const WCHAR media_package[] = {
796         'M','e','d','i','a','P','a','c','k','a','g','e',0
797     };
798
799     TRACE("%s %s %x %x %s %s\n", debugstr_w(szProduct), debugstr_w(szUserSid),
800             dwContext, dwOptions, debugstr_w(szProperty), debugstr_w(szValue));
801
802     if (!szProduct || !squash_guid(szProduct, squished_pc))
803         return ERROR_INVALID_PARAMETER;
804
805     if (!szProperty)
806         return ERROR_INVALID_PARAMETER;
807
808     if (!szValue)
809         return ERROR_UNKNOWN_PROPERTY;
810
811     if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid)
812         return ERROR_INVALID_PARAMETER;
813
814     if (dwOptions & MSICODE_PATCH)
815     {
816         FIXME("Unhandled options MSICODE_PATCH\n");
817         return ERROR_UNKNOWN_PATCH;
818     }
819
820     property = szProperty;
821     if (!lstrcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW))
822         property = media_package;
823
824     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
825     if (rc != ERROR_SUCCESS)
826         return rc;
827
828     if (lstrcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW) &&
829         dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL))
830     {
831         RegCloseKey(sourcekey);
832         return ERROR_INVALID_PARAMETER;
833     }
834
835     if (!lstrcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) ||
836         !lstrcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW))
837     {
838         rc = OpenMediaSubkey(sourcekey, &media, TRUE);
839         if (rc == ERROR_SUCCESS)
840         {
841             rc = msi_reg_set_val_str(media, property, szValue);
842             RegCloseKey(media);
843         }
844     }
845     else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0)
846     {
847         DWORD size = (lstrlenW(szValue) + 1) * sizeof(WCHAR);
848         rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0,
849                 REG_SZ, (const BYTE *)szValue, size);
850         if (rc != ERROR_SUCCESS)
851             rc = ERROR_UNKNOWN_PROPERTY;
852     }
853     else if (!lstrcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW))
854     {
855         if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
856             rc = ERROR_INVALID_PARAMETER;
857         else
858             rc = msi_set_last_used_source(szProduct, szUserSid, dwContext,
859                                           dwOptions, szValue);
860     }
861     else
862         rc = ERROR_UNKNOWN_PROPERTY;
863
864     RegCloseKey(sourcekey);
865     return rc;
866 }
867
868 /******************************************************************
869  *  MsiSourceListAddSourceW (MSI.@)
870  */
871 UINT WINAPI MsiSourceListAddSourceW( LPCWSTR szProduct, LPCWSTR szUserName,
872         DWORD dwReserved, LPCWSTR szSource)
873 {
874     WCHAR squished_pc[GUID_SIZE];
875     INT ret;
876     LPWSTR sidstr = NULL;
877     DWORD sidsize = 0;
878     DWORD domsize = 0;
879     DWORD context;
880     HKEY hkey = 0;
881     UINT r;
882
883     TRACE("%s %s %s\n", debugstr_w(szProduct), debugstr_w(szUserName), debugstr_w(szSource));
884
885     if (!szSource || !*szSource)
886         return ERROR_INVALID_PARAMETER;
887
888     if (dwReserved != 0)
889         return ERROR_INVALID_PARAMETER;
890
891     if (!szProduct || !squash_guid(szProduct, squished_pc))
892         return ERROR_INVALID_PARAMETER;
893
894     if (!szUserName || !*szUserName)
895         context = MSIINSTALLCONTEXT_MACHINE;
896     else
897     {
898         if (LookupAccountNameW(NULL, szUserName, NULL, &sidsize, NULL, &domsize, NULL))
899         {
900             PSID psid = msi_alloc(sidsize);
901
902             if (LookupAccountNameW(NULL, szUserName, psid, &sidsize, NULL, &domsize, NULL))
903                 ConvertSidToStringSidW(psid, &sidstr);
904
905             msi_free(psid);
906         }
907
908         r = MSIREG_OpenProductKey(szProduct, NULL,
909                                   MSIINSTALLCONTEXT_USERMANAGED, &hkey, FALSE);
910         if (r == ERROR_SUCCESS)
911             context = MSIINSTALLCONTEXT_USERMANAGED;
912         else
913         {
914             r = MSIREG_OpenProductKey(szProduct, NULL,
915                                       MSIINSTALLCONTEXT_USERUNMANAGED,
916                                       &hkey, FALSE);
917             if (r != ERROR_SUCCESS)
918                 return ERROR_UNKNOWN_PRODUCT;
919
920             context = MSIINSTALLCONTEXT_USERUNMANAGED;
921         }
922
923         RegCloseKey(hkey);
924     }
925
926     ret = MsiSourceListAddSourceExW(szProduct, sidstr, 
927         context, MSISOURCETYPE_NETWORK, szSource, 0);
928
929     if (sidstr)
930         LocalFree(sidstr);
931
932     return ret;
933 }
934
935 /******************************************************************
936  *  MsiSourceListAddSourceA (MSI.@)
937  */
938 UINT WINAPI MsiSourceListAddSourceA( LPCSTR szProduct, LPCSTR szUserName,
939         DWORD dwReserved, LPCSTR szSource)
940 {
941     INT ret;
942     LPWSTR szwproduct;
943     LPWSTR szwusername;
944     LPWSTR szwsource;
945
946     szwproduct = strdupAtoW( szProduct );
947     szwusername = strdupAtoW( szUserName );
948     szwsource = strdupAtoW( szSource );
949
950     ret = MsiSourceListAddSourceW(szwproduct, szwusername, dwReserved, szwsource);
951
952     msi_free(szwproduct);
953     msi_free(szwusername);
954     msi_free(szwsource);
955
956     return ret;
957 }
958
959 /******************************************************************
960  *  MsiSourceListAddSourceExA (MSI.@)
961  */
962 UINT WINAPI MsiSourceListAddSourceExA(LPCSTR szProduct, LPCSTR szUserSid,
963         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCSTR szSource, DWORD dwIndex)
964 {
965     UINT ret;
966     LPWSTR product, usersid, source;
967
968     product = strdupAtoW(szProduct);
969     usersid = strdupAtoW(szUserSid);
970     source = strdupAtoW(szSource);
971
972     ret = MsiSourceListAddSourceExW(product, usersid, dwContext,
973                                     dwOptions, source, dwIndex);
974
975     msi_free(product);
976     msi_free(usersid);
977     msi_free(source);
978
979     return ret;
980 }
981
982 static void free_source_list(struct list *sourcelist)
983 {
984     while (!list_empty(sourcelist))
985     {
986         media_info *info = LIST_ENTRY(list_head(sourcelist), media_info, entry);
987         list_remove(&info->entry);
988         msi_free(info->path);
989         msi_free(info);
990     }
991 }
992
993 static void add_source_to_list(struct list *sourcelist, media_info *info,
994                                DWORD *index)
995 {
996     media_info *iter;
997     BOOL found = FALSE;
998     static const WCHAR fmt[] = {'%','i',0};
999
1000     if (index) *index = 0;
1001
1002     if (list_empty(sourcelist))
1003     {
1004         list_add_head(sourcelist, &info->entry);
1005         return;
1006     }
1007
1008     LIST_FOR_EACH_ENTRY(iter, sourcelist, media_info, entry)
1009     {
1010         if (!found && info->index < iter->index)
1011         {
1012             found = TRUE;
1013             list_add_before(&iter->entry, &info->entry);
1014         }
1015
1016         /* update the rest of the list */
1017         if (found)
1018             sprintfW(iter->szIndex, fmt, ++iter->index);
1019         else if (index)
1020             (*index)++;
1021     }
1022
1023     if (!found)
1024         list_add_after(&iter->entry, &info->entry);
1025 }
1026
1027 static UINT fill_source_list(struct list *sourcelist, HKEY sourcekey, DWORD *count)
1028 {
1029     UINT r = ERROR_SUCCESS;
1030     DWORD index = 0;
1031     WCHAR name[10];
1032     DWORD size, val_size;
1033     media_info *entry;
1034
1035     *count = 0;
1036
1037     while (r == ERROR_SUCCESS)
1038     {
1039         size = sizeof(name) / sizeof(name[0]);
1040         r = RegEnumValueW(sourcekey, index, name, &size, NULL, NULL, NULL, &val_size);
1041         if (r != ERROR_SUCCESS)
1042             return r;
1043
1044         entry = msi_alloc(sizeof(media_info));
1045         if (!entry)
1046             goto error;
1047
1048         entry->path = msi_alloc(val_size);
1049         if (!entry->path)
1050         {
1051             msi_free(entry);
1052             goto error;
1053         }
1054
1055         lstrcpyW(entry->szIndex, name);
1056         entry->index = atoiW(name);
1057
1058         size++;
1059         r = RegEnumValueW(sourcekey, index, name, &size, NULL,
1060                           NULL, (LPBYTE)entry->path, &val_size);
1061         if (r != ERROR_SUCCESS)
1062         {
1063             msi_free(entry->path);
1064             msi_free(entry);
1065             goto error;
1066         }
1067
1068         index = ++(*count);
1069         add_source_to_list(sourcelist, entry, NULL);
1070     }
1071
1072 error:
1073     *count = -1;
1074     free_source_list(sourcelist);
1075     return ERROR_OUTOFMEMORY;
1076 }
1077
1078 /******************************************************************
1079  *  MsiSourceListAddSourceExW (MSI.@)
1080  */
1081 UINT WINAPI MsiSourceListAddSourceExW( LPCWSTR szProduct, LPCWSTR szUserSid,
1082         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szSource, 
1083         DWORD dwIndex)
1084 {
1085     HKEY sourcekey;
1086     HKEY typekey;
1087     UINT rc;
1088     struct list sourcelist;
1089     media_info *info;
1090     WCHAR squished_pc[GUID_SIZE];
1091     WCHAR name[10];
1092     LPWSTR source;
1093     LPCWSTR postfix;
1094     DWORD size, count;
1095     DWORD index;
1096
1097     static const WCHAR fmt[] = {'%','i',0};
1098
1099     TRACE("%s %s %x %x %s %i\n", debugstr_w(szProduct), debugstr_w(szUserSid),
1100           dwContext, dwOptions, debugstr_w(szSource), dwIndex);
1101
1102     if (!szProduct || !squash_guid(szProduct, squished_pc))
1103         return ERROR_INVALID_PARAMETER;
1104
1105     if (!szSource || !*szSource)
1106         return ERROR_INVALID_PARAMETER;
1107
1108     if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
1109         return ERROR_INVALID_PARAMETER;
1110
1111     if (dwOptions & MSICODE_PATCH)
1112     {
1113         FIXME("Unhandled options MSICODE_PATCH\n");
1114         return ERROR_FUNCTION_FAILED;
1115     }
1116
1117     if (szUserSid && (dwContext & MSIINSTALLCONTEXT_MACHINE))
1118         return ERROR_INVALID_PARAMETER;
1119
1120     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
1121     if (rc != ERROR_SUCCESS)
1122         return rc;
1123
1124     if (dwOptions & MSISOURCETYPE_NETWORK)
1125         rc = OpenNetworkSubkey(sourcekey, &typekey, TRUE);
1126     else if (dwOptions & MSISOURCETYPE_URL)
1127         rc = OpenURLSubkey(sourcekey, &typekey, TRUE);
1128     else if (dwOptions & MSISOURCETYPE_MEDIA)
1129         rc = OpenMediaSubkey(sourcekey, &typekey, TRUE);
1130     else
1131     {
1132         ERR("unknown media type: %08x\n", dwOptions);
1133         RegCloseKey(sourcekey);
1134         return ERROR_FUNCTION_FAILED;
1135     }
1136
1137     postfix = (dwOptions & MSISOURCETYPE_NETWORK) ? szBackSlash : szForwardSlash;
1138     if (szSource[lstrlenW(szSource) - 1] == *postfix)
1139         source = strdupW(szSource);
1140     else
1141     {
1142         size = lstrlenW(szSource) + 2;
1143         source = msi_alloc(size * sizeof(WCHAR));
1144         lstrcpyW(source, szSource);
1145         lstrcatW(source, postfix);
1146     }
1147
1148     list_init(&sourcelist);
1149     rc = fill_source_list(&sourcelist, typekey, &count);
1150     if (rc != ERROR_NO_MORE_ITEMS)
1151         return rc;
1152
1153     size = (lstrlenW(source) + 1) * sizeof(WCHAR);
1154
1155     if (count == 0)
1156     {
1157         rc = RegSetValueExW(typekey, szOne, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
1158         goto done;
1159     }
1160     else if (dwIndex > count || dwIndex == 0)
1161     {
1162         sprintfW(name, fmt, count + 1);
1163         rc = RegSetValueExW(typekey, name, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
1164         goto done;
1165     }
1166     else
1167     {
1168         sprintfW(name, fmt, dwIndex);
1169         info = msi_alloc(sizeof(media_info));
1170         if (!info)
1171         {
1172             rc = ERROR_OUTOFMEMORY;
1173             goto done;
1174         }
1175
1176         info->path = strdupW(source);
1177         lstrcpyW(info->szIndex, name);
1178         info->index = dwIndex;
1179         add_source_to_list(&sourcelist, info, &index);
1180
1181         LIST_FOR_EACH_ENTRY(info, &sourcelist, media_info, entry)
1182         {
1183             if (info->index < index)
1184                 continue;
1185
1186             size = (lstrlenW(info->path) + 1) * sizeof(WCHAR);
1187             rc = RegSetValueExW(typekey, info->szIndex, 0,
1188                                 REG_EXPAND_SZ, (LPBYTE)info->path, size);
1189             if (rc != ERROR_SUCCESS)
1190                 goto done;
1191         }
1192     }
1193
1194 done:
1195     free_source_list(&sourcelist);
1196     msi_free(source);
1197     RegCloseKey(typekey);
1198     RegCloseKey(sourcekey);
1199     return rc;
1200 }
1201
1202 /******************************************************************
1203  *  MsiSourceListAddMediaDiskA (MSI.@)
1204  */
1205 UINT WINAPI MsiSourceListAddMediaDiskA(LPCSTR szProduct, LPCSTR szUserSid,
1206         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId,
1207         LPCSTR szVolumeLabel, LPCSTR szDiskPrompt)
1208 {
1209     UINT r;
1210     LPWSTR product = NULL;
1211     LPWSTR usersid = NULL;
1212     LPWSTR volume = NULL;
1213     LPWSTR prompt = NULL;
1214
1215     if (szProduct) product = strdupAtoW(szProduct);
1216     if (szUserSid) usersid = strdupAtoW(szUserSid);
1217     if (szVolumeLabel) volume = strdupAtoW(szVolumeLabel);
1218     if (szDiskPrompt) prompt = strdupAtoW(szDiskPrompt);
1219
1220     r = MsiSourceListAddMediaDiskW(product, usersid, dwContext, dwOptions,
1221                                      dwDiskId, volume, prompt);
1222
1223     msi_free(product);
1224     msi_free(usersid);
1225     msi_free(volume);
1226     msi_free(prompt);
1227
1228     return r;
1229 }
1230
1231 /******************************************************************
1232  *  MsiSourceListAddMediaDiskW (MSI.@)
1233  */
1234 UINT WINAPI MsiSourceListAddMediaDiskW(LPCWSTR szProduct, LPCWSTR szUserSid, 
1235         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId, 
1236         LPCWSTR szVolumeLabel, LPCWSTR szDiskPrompt)
1237 {
1238     HKEY sourcekey;
1239     HKEY mediakey;
1240     UINT rc;
1241     WCHAR szIndex[10];
1242     WCHAR squished_pc[GUID_SIZE];
1243     LPWSTR buffer;
1244     DWORD size;
1245
1246     static const WCHAR fmt[] = {'%','i',0};
1247
1248     TRACE("%s %s %x %x %i %s %s\n", debugstr_w(szProduct),
1249             debugstr_w(szUserSid), dwContext, dwOptions, dwDiskId,
1250             debugstr_w(szVolumeLabel), debugstr_w(szDiskPrompt));
1251
1252     if (!szProduct || !squash_guid(szProduct, squished_pc))
1253         return ERROR_INVALID_PARAMETER;
1254
1255     if (dwOptions != MSICODE_PRODUCT && dwOptions != MSICODE_PATCH)
1256         return ERROR_INVALID_PARAMETER;
1257
1258     if ((szVolumeLabel && !*szVolumeLabel) || (szDiskPrompt && !*szDiskPrompt))
1259         return ERROR_INVALID_PARAMETER;
1260
1261     if ((dwContext & MSIINSTALLCONTEXT_MACHINE) && szUserSid)
1262         return ERROR_INVALID_PARAMETER;
1263
1264     if (dwOptions & MSICODE_PATCH)
1265     {
1266         FIXME("Unhandled options MSICODE_PATCH\n");
1267         return ERROR_FUNCTION_FAILED;
1268     }
1269
1270     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
1271     if (rc != ERROR_SUCCESS)
1272         return rc;
1273
1274     OpenMediaSubkey(sourcekey, &mediakey, TRUE);
1275
1276     sprintfW(szIndex, fmt, dwDiskId);
1277
1278     size = 2;
1279     if (szVolumeLabel) size += lstrlenW(szVolumeLabel);
1280     if (szDiskPrompt) size += lstrlenW(szDiskPrompt);
1281
1282     size *= sizeof(WCHAR);
1283     buffer = msi_alloc(size);
1284     *buffer = '\0';
1285
1286     if (szVolumeLabel) lstrcpyW(buffer, szVolumeLabel);
1287     lstrcatW(buffer, szSemiColon);
1288     if (szDiskPrompt) lstrcatW(buffer, szDiskPrompt);
1289
1290     RegSetValueExW(mediakey, szIndex, 0, REG_SZ, (LPBYTE)buffer, size);
1291     msi_free(buffer);
1292
1293     RegCloseKey(sourcekey);
1294     RegCloseKey(mediakey);
1295
1296     return ERROR_SUCCESS;
1297 }
1298
1299 /******************************************************************
1300  *  MsiSourceListClearAllA (MSI.@)
1301  */
1302 UINT WINAPI MsiSourceListClearAllA( LPCSTR szProduct, LPCSTR szUserName, DWORD dwReserved )
1303 {
1304     FIXME("(%s %s %d)\n", debugstr_a(szProduct), debugstr_a(szUserName), dwReserved);
1305     return ERROR_SUCCESS;
1306 }
1307
1308 /******************************************************************
1309  *  MsiSourceListClearAllW (MSI.@)
1310  */
1311 UINT WINAPI MsiSourceListClearAllW( LPCWSTR szProduct, LPCWSTR szUserName, DWORD dwReserved )
1312 {
1313     FIXME("(%s %s %d)\n", debugstr_w(szProduct), debugstr_w(szUserName), dwReserved);
1314     return ERROR_SUCCESS;
1315 }
1316
1317 /******************************************************************
1318  *  MsiSourceListClearAllExA (MSI.@)
1319  */
1320 UINT WINAPI MsiSourceListClearAllExA( LPCSTR szProduct, LPCSTR szUserSid,
1321     MSIINSTALLCONTEXT dwContext, DWORD dwOptions )
1322 {
1323     FIXME("(%s %s %d %08x)\n", debugstr_a(szProduct), debugstr_a(szUserSid),
1324           dwContext, dwOptions);
1325     return ERROR_SUCCESS;
1326 }
1327
1328 /******************************************************************
1329  *  MsiSourceListClearAllExW (MSI.@)
1330  */
1331 UINT WINAPI MsiSourceListClearAllExW( LPCWSTR szProduct, LPCWSTR szUserSid,
1332     MSIINSTALLCONTEXT dwContext, DWORD dwOptions )
1333 {
1334     FIXME("(%s %s %d %08x)\n", debugstr_w(szProduct), debugstr_w(szUserSid),
1335           dwContext, dwOptions);
1336     return ERROR_SUCCESS;
1337 }
1338
1339 /******************************************************************
1340  *  MsiSourceListClearSourceA (MSI.@)
1341  */
1342 UINT WINAPI MsiSourceListClearSourceA(LPCSTR szProductCodeOrPatchCode, LPCSTR szUserSid,
1343                                       MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
1344                                       LPCSTR szSource)
1345 {
1346     FIXME("(%s %s %x %x %s)\n", debugstr_a(szProductCodeOrPatchCode), debugstr_a(szUserSid),
1347           dwContext, dwOptions, debugstr_a(szSource));
1348     return ERROR_SUCCESS;
1349 }
1350
1351 /******************************************************************
1352  *  MsiSourceListClearSourceW (MSI.@)
1353  */
1354 UINT WINAPI MsiSourceListClearSourceW(LPCWSTR szProductCodeOrPatchCode, LPCWSTR szUserSid,
1355                                       MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
1356                                       LPCWSTR szSource)
1357 {
1358     FIXME("(%s %s %x %x %s)\n", debugstr_w(szProductCodeOrPatchCode), debugstr_w(szUserSid),
1359           dwContext, dwOptions, debugstr_w(szSource));
1360     return ERROR_SUCCESS;
1361 }