msi/tests: Skip SourceDir tests on win9x.
[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     rc = OpenSourceKey(szProduct, &sourcekey, dwOptions, dwContext, FALSE);
592     if (rc != ERROR_SUCCESS)
593         return rc;
594
595     if (!lstrcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) ||
596         !lstrcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW))
597     {
598         rc = OpenMediaSubkey(sourcekey, &media, FALSE);
599         if (rc != ERROR_SUCCESS)
600         {
601             RegCloseKey(sourcekey);
602             return ERROR_SUCCESS;
603         }
604
605         if (!lstrcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW))
606             szProperty = mediapack;
607
608         RegQueryValueExW(media, szProperty, 0, 0, (LPBYTE)szValue, pcchValue);
609         RegCloseKey(media);
610     }
611     else if (!lstrcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW) ||
612              !lstrcmpW(szProperty, INSTALLPROPERTY_LASTUSEDTYPEW))
613     {
614         rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
615                               0, 0, NULL, &size);
616         if (rc != ERROR_SUCCESS)
617         {
618             RegCloseKey(sourcekey);
619             return ERROR_SUCCESS;
620         }
621
622         source = msi_alloc(size);
623         RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
624                          0, 0, (LPBYTE)source, &size);
625
626         if (!*source)
627         {
628             msi_free(source);
629             RegCloseKey(sourcekey);
630             return ERROR_SUCCESS;
631         }
632
633         if (!lstrcmpW(szProperty, INSTALLPROPERTY_LASTUSEDTYPEW))
634         {
635             if (*source != 'n' && *source != 'u' && *source != 'm')
636             {
637                 msi_free(source);
638                 RegCloseKey(sourcekey);
639                 return ERROR_SUCCESS;
640             }
641
642             ptr = source;
643             source[1] = '\0';
644         }
645         else
646         {
647             ptr = strrchrW(source, ';');
648             if (!ptr)
649                 ptr = source;
650             else
651                 ptr++;
652         }
653
654         if (szValue)
655         {
656             if (strlenW(ptr) < *pcchValue)
657                 lstrcpyW(szValue, ptr);
658             else
659                 rc = ERROR_MORE_DATA;
660         }
661
662         *pcchValue = lstrlenW(ptr);
663         msi_free(source);
664     }
665     else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0)
666     {
667         *pcchValue = *pcchValue * sizeof(WCHAR);
668         rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, 0,
669                               (LPBYTE)szValue, pcchValue);
670         if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
671         {
672             *pcchValue = 0;
673             rc = ERROR_SUCCESS;
674         }
675         else
676         {
677             if (*pcchValue)
678                 *pcchValue = (*pcchValue - 1) / sizeof(WCHAR);
679             if (szValue)
680                 szValue[*pcchValue] = '\0';
681         }
682     }
683     else
684     {
685         FIXME("Unknown property %s\n",debugstr_w(szProperty));
686         rc = ERROR_UNKNOWN_PROPERTY;
687     }
688
689     RegCloseKey(sourcekey);
690     return rc;
691 }
692
693 /******************************************************************
694  *  MsiSourceListSetInfoA   (MSI.@)
695  */
696 UINT WINAPI MsiSourceListSetInfoA(LPCSTR szProduct, LPCSTR szUserSid,
697                                   MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
698                                   LPCSTR szProperty, LPCSTR szValue)
699 {
700     UINT ret;
701     LPWSTR product = NULL;
702     LPWSTR usersid = NULL;
703     LPWSTR property = NULL;
704     LPWSTR value = NULL;
705
706     if (szProduct) product = strdupAtoW(szProduct);
707     if (szUserSid) usersid = strdupAtoW(szUserSid);
708     if (szProperty) property = strdupAtoW(szProperty);
709     if (szValue) value = strdupAtoW(szValue);
710
711     ret = MsiSourceListSetInfoW(product, usersid, dwContext, dwOptions,
712                                 property, value);
713
714     msi_free(product);
715     msi_free(usersid);
716     msi_free(property);
717     msi_free(value);
718
719     return ret;
720 }
721
722 UINT msi_set_last_used_source(LPCWSTR product, LPCWSTR usersid,
723                               MSIINSTALLCONTEXT context, DWORD options,
724                               LPCWSTR value)
725 {
726     HKEY source;
727     LPWSTR buffer;
728     WCHAR typechar;
729     DWORD size;
730     UINT r;
731     int index = 1;
732
733     static const WCHAR format[] = {'%','c',';','%','i',';','%','s',0};
734
735     if (options & MSISOURCETYPE_NETWORK)
736         typechar = 'n';
737     else if (options & MSISOURCETYPE_URL)
738         typechar = 'u';
739     else if (options & MSISOURCETYPE_MEDIA)
740         typechar = 'm';
741     else
742         return ERROR_INVALID_PARAMETER;
743
744     if (!(options & MSISOURCETYPE_MEDIA))
745     {
746         r = MsiSourceListAddSourceExW(product, usersid, context,
747                                       options, value, 0);
748         if (r != ERROR_SUCCESS)
749             return r;
750
751         index = 0;
752         while ((r = MsiSourceListEnumSourcesW(product, usersid, context, options,
753                                               index, NULL, NULL)) == ERROR_SUCCESS)
754             index++;
755
756         if (r != ERROR_NO_MORE_ITEMS)
757             return r;
758     }
759
760     size = (lstrlenW(format) + lstrlenW(value) + 7) * sizeof(WCHAR);
761     buffer = msi_alloc(size);
762     if (!buffer)
763         return ERROR_OUTOFMEMORY;
764
765     r = OpenSourceKey(product, &source, MSICODE_PRODUCT, context, FALSE);
766     if (r != ERROR_SUCCESS)
767         return r;
768
769     sprintfW(buffer, format, typechar, index, value);
770
771     size = (lstrlenW(buffer) + 1) * sizeof(WCHAR);
772     r = RegSetValueExW(source, INSTALLPROPERTY_LASTUSEDSOURCEW, 0,
773                        REG_SZ, (LPBYTE)buffer, size);
774     msi_free(buffer);
775
776     RegCloseKey(source);
777     return r;
778 }
779
780 /******************************************************************
781  *  MsiSourceListSetInfoW   (MSI.@)
782  */
783 UINT WINAPI MsiSourceListSetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
784                                    MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
785                                    LPCWSTR szProperty, LPCWSTR szValue)
786 {
787     WCHAR squished_pc[GUID_SIZE];
788     HKEY sourcekey, media;
789     LPCWSTR property;
790     UINT rc;
791
792     static const WCHAR media_package[] = {
793         'M','e','d','i','a','P','a','c','k','a','g','e',0
794     };
795
796     TRACE("%s %s %x %x %s %s\n", debugstr_w(szProduct), debugstr_w(szUserSid),
797             dwContext, dwOptions, debugstr_w(szProperty), debugstr_w(szValue));
798
799     if (!szProduct || !squash_guid(szProduct, squished_pc))
800         return ERROR_INVALID_PARAMETER;
801
802     if (!szProperty)
803         return ERROR_INVALID_PARAMETER;
804
805     if (!szValue)
806         return ERROR_UNKNOWN_PROPERTY;
807
808     if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid)
809         return ERROR_INVALID_PARAMETER;
810
811     if (dwOptions & MSICODE_PATCH)
812     {
813         FIXME("Unhandled options MSICODE_PATCH\n");
814         return ERROR_UNKNOWN_PATCH;
815     }
816
817     property = szProperty;
818     if (!lstrcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW))
819         property = media_package;
820
821     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
822     if (rc != ERROR_SUCCESS)
823         return rc;
824
825     if (lstrcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW) &&
826         dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL))
827     {
828         RegCloseKey(sourcekey);
829         return ERROR_INVALID_PARAMETER;
830     }
831
832     if (!lstrcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) ||
833         !lstrcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW))
834     {
835         rc = OpenMediaSubkey(sourcekey, &media, TRUE);
836         if (rc == ERROR_SUCCESS)
837         {
838             rc = msi_reg_set_val_str(media, property, szValue);
839             RegCloseKey(media);
840         }
841     }
842     else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0)
843     {
844         DWORD size = (lstrlenW(szValue) + 1) * sizeof(WCHAR);
845         rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0,
846                 REG_SZ, (const BYTE *)szValue, size);
847         if (rc != ERROR_SUCCESS)
848             rc = ERROR_UNKNOWN_PROPERTY;
849     }
850     else if (!lstrcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW))
851     {
852         if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
853             rc = ERROR_INVALID_PARAMETER;
854         else
855             rc = msi_set_last_used_source(szProduct, szUserSid, dwContext,
856                                           dwOptions, szValue);
857     }
858     else
859         rc = ERROR_UNKNOWN_PROPERTY;
860
861     RegCloseKey(sourcekey);
862     return rc;
863 }
864
865 /******************************************************************
866  *  MsiSourceListAddSourceW (MSI.@)
867  */
868 UINT WINAPI MsiSourceListAddSourceW( LPCWSTR szProduct, LPCWSTR szUserName,
869         DWORD dwReserved, LPCWSTR szSource)
870 {
871     WCHAR squished_pc[GUID_SIZE];
872     INT ret;
873     LPWSTR sidstr = NULL;
874     DWORD sidsize = 0;
875     DWORD domsize = 0;
876     DWORD context;
877     HKEY hkey = 0;
878     UINT r;
879
880     TRACE("%s %s %s\n", debugstr_w(szProduct), debugstr_w(szUserName), debugstr_w(szSource));
881
882     if (!szSource || !*szSource)
883         return ERROR_INVALID_PARAMETER;
884
885     if (dwReserved != 0)
886         return ERROR_INVALID_PARAMETER;
887
888     if (!szProduct || !squash_guid(szProduct, squished_pc))
889         return ERROR_INVALID_PARAMETER;
890
891     if (!szUserName || !*szUserName)
892         context = MSIINSTALLCONTEXT_MACHINE;
893     else
894     {
895         if (LookupAccountNameW(NULL, szUserName, NULL, &sidsize, NULL, &domsize, NULL))
896         {
897             PSID psid = msi_alloc(sidsize);
898
899             if (LookupAccountNameW(NULL, szUserName, psid, &sidsize, NULL, &domsize, NULL))
900                 ConvertSidToStringSidW(psid, &sidstr);
901
902             msi_free(psid);
903         }
904
905         r = MSIREG_OpenProductKey(szProduct, NULL,
906                                   MSIINSTALLCONTEXT_USERMANAGED, &hkey, FALSE);
907         if (r == ERROR_SUCCESS)
908             context = MSIINSTALLCONTEXT_USERMANAGED;
909         else
910         {
911             r = MSIREG_OpenProductKey(szProduct, NULL,
912                                       MSIINSTALLCONTEXT_USERUNMANAGED,
913                                       &hkey, FALSE);
914             if (r != ERROR_SUCCESS)
915                 return ERROR_UNKNOWN_PRODUCT;
916
917             context = MSIINSTALLCONTEXT_USERUNMANAGED;
918         }
919
920         RegCloseKey(hkey);
921     }
922
923     ret = MsiSourceListAddSourceExW(szProduct, sidstr, 
924         context, MSISOURCETYPE_NETWORK, szSource, 0);
925
926     if (sidstr)
927         LocalFree(sidstr);
928
929     return ret;
930 }
931
932 /******************************************************************
933  *  MsiSourceListAddSourceA (MSI.@)
934  */
935 UINT WINAPI MsiSourceListAddSourceA( LPCSTR szProduct, LPCSTR szUserName,
936         DWORD dwReserved, LPCSTR szSource)
937 {
938     INT ret;
939     LPWSTR szwproduct;
940     LPWSTR szwusername;
941     LPWSTR szwsource;
942
943     szwproduct = strdupAtoW( szProduct );
944     szwusername = strdupAtoW( szUserName );
945     szwsource = strdupAtoW( szSource );
946
947     ret = MsiSourceListAddSourceW(szwproduct, szwusername, dwReserved, szwsource);
948
949     msi_free(szwproduct);
950     msi_free(szwusername);
951     msi_free(szwsource);
952
953     return ret;
954 }
955
956 /******************************************************************
957  *  MsiSourceListAddSourceExA (MSI.@)
958  */
959 UINT WINAPI MsiSourceListAddSourceExA(LPCSTR szProduct, LPCSTR szUserSid,
960         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCSTR szSource, DWORD dwIndex)
961 {
962     UINT ret;
963     LPWSTR product, usersid, source;
964
965     product = strdupAtoW(szProduct);
966     usersid = strdupAtoW(szUserSid);
967     source = strdupAtoW(szSource);
968
969     ret = MsiSourceListAddSourceExW(product, usersid, dwContext,
970                                     dwOptions, source, dwIndex);
971
972     msi_free(product);
973     msi_free(usersid);
974     msi_free(source);
975
976     return ret;
977 }
978
979 static void free_source_list(struct list *sourcelist)
980 {
981     while (!list_empty(sourcelist))
982     {
983         media_info *info = LIST_ENTRY(list_head(sourcelist), media_info, entry);
984         list_remove(&info->entry);
985         msi_free(info->path);
986         msi_free(info);
987     }
988 }
989
990 static void add_source_to_list(struct list *sourcelist, media_info *info,
991                                DWORD *index)
992 {
993     media_info *iter;
994     BOOL found = FALSE;
995     static const WCHAR fmt[] = {'%','i',0};
996
997     if (index) *index = 0;
998
999     if (list_empty(sourcelist))
1000     {
1001         list_add_head(sourcelist, &info->entry);
1002         return;
1003     }
1004
1005     LIST_FOR_EACH_ENTRY(iter, sourcelist, media_info, entry)
1006     {
1007         if (!found && info->index < iter->index)
1008         {
1009             found = TRUE;
1010             list_add_before(&iter->entry, &info->entry);
1011         }
1012
1013         /* update the rest of the list */
1014         if (found)
1015             sprintfW(iter->szIndex, fmt, ++iter->index);
1016         else if (index)
1017             (*index)++;
1018     }
1019
1020     if (!found)
1021         list_add_after(&iter->entry, &info->entry);
1022 }
1023
1024 static UINT fill_source_list(struct list *sourcelist, HKEY sourcekey, DWORD *count)
1025 {
1026     UINT r = ERROR_SUCCESS;
1027     DWORD index = 0;
1028     WCHAR name[10];
1029     DWORD size, val_size;
1030     media_info *entry;
1031
1032     *count = 0;
1033
1034     while (r == ERROR_SUCCESS)
1035     {
1036         size = sizeof(name) / sizeof(name[0]);
1037         r = RegEnumValueW(sourcekey, index, name, &size, NULL, NULL, NULL, &val_size);
1038         if (r != ERROR_SUCCESS)
1039             return r;
1040
1041         entry = msi_alloc(sizeof(media_info));
1042         if (!entry)
1043             goto error;
1044
1045         entry->path = msi_alloc(val_size);
1046         if (!entry->path)
1047         {
1048             msi_free(entry);
1049             goto error;
1050         }
1051
1052         lstrcpyW(entry->szIndex, name);
1053         entry->index = atoiW(name);
1054
1055         size++;
1056         r = RegEnumValueW(sourcekey, index, name, &size, NULL,
1057                           NULL, (LPBYTE)entry->path, &val_size);
1058         if (r != ERROR_SUCCESS)
1059         {
1060             msi_free(entry->path);
1061             msi_free(entry);
1062             goto error;
1063         }
1064
1065         index = ++(*count);
1066         add_source_to_list(sourcelist, entry, NULL);
1067     }
1068
1069 error:
1070     *count = -1;
1071     free_source_list(sourcelist);
1072     return ERROR_OUTOFMEMORY;
1073 }
1074
1075 /******************************************************************
1076  *  MsiSourceListAddSourceExW (MSI.@)
1077  */
1078 UINT WINAPI MsiSourceListAddSourceExW( LPCWSTR szProduct, LPCWSTR szUserSid,
1079         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szSource, 
1080         DWORD dwIndex)
1081 {
1082     HKEY sourcekey;
1083     HKEY typekey;
1084     UINT rc;
1085     struct list sourcelist;
1086     media_info *info;
1087     WCHAR squished_pc[GUID_SIZE];
1088     WCHAR name[10];
1089     LPWSTR source;
1090     LPCWSTR postfix;
1091     DWORD size, count;
1092     DWORD index;
1093
1094     static const WCHAR fmt[] = {'%','i',0};
1095
1096     TRACE("%s %s %x %x %s %i\n", debugstr_w(szProduct), debugstr_w(szUserSid),
1097           dwContext, dwOptions, debugstr_w(szSource), dwIndex);
1098
1099     if (!szProduct || !squash_guid(szProduct, squished_pc))
1100         return ERROR_INVALID_PARAMETER;
1101
1102     if (!szSource || !*szSource)
1103         return ERROR_INVALID_PARAMETER;
1104
1105     if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
1106         return ERROR_INVALID_PARAMETER;
1107
1108     if (dwOptions & MSICODE_PATCH)
1109     {
1110         FIXME("Unhandled options MSICODE_PATCH\n");
1111         return ERROR_FUNCTION_FAILED;
1112     }
1113
1114     if (szUserSid && (dwContext & MSIINSTALLCONTEXT_MACHINE))
1115         return ERROR_INVALID_PARAMETER;
1116
1117     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
1118     if (rc != ERROR_SUCCESS)
1119         return rc;
1120
1121     if (dwOptions & MSISOURCETYPE_NETWORK)
1122         rc = OpenNetworkSubkey(sourcekey, &typekey, TRUE);
1123     else if (dwOptions & MSISOURCETYPE_URL)
1124         rc = OpenURLSubkey(sourcekey, &typekey, TRUE);
1125     else if (dwOptions & MSISOURCETYPE_MEDIA)
1126         rc = OpenMediaSubkey(sourcekey, &typekey, TRUE);
1127     else
1128     {
1129         ERR("unknown media type: %08x\n", dwOptions);
1130         RegCloseKey(sourcekey);
1131         return ERROR_FUNCTION_FAILED;
1132     }
1133
1134     postfix = (dwOptions & MSISOURCETYPE_NETWORK) ? szBackSlash : szForwardSlash;
1135     if (szSource[lstrlenW(szSource) - 1] == *postfix)
1136         source = strdupW(szSource);
1137     else
1138     {
1139         size = lstrlenW(szSource) + 2;
1140         source = msi_alloc(size * sizeof(WCHAR));
1141         lstrcpyW(source, szSource);
1142         lstrcatW(source, postfix);
1143     }
1144
1145     list_init(&sourcelist);
1146     rc = fill_source_list(&sourcelist, typekey, &count);
1147     if (rc != ERROR_NO_MORE_ITEMS)
1148         return rc;
1149
1150     size = (lstrlenW(source) + 1) * sizeof(WCHAR);
1151
1152     if (count == 0)
1153     {
1154         rc = RegSetValueExW(typekey, szOne, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
1155         goto done;
1156     }
1157     else if (dwIndex > count || dwIndex == 0)
1158     {
1159         sprintfW(name, fmt, count + 1);
1160         rc = RegSetValueExW(typekey, name, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
1161         goto done;
1162     }
1163     else
1164     {
1165         sprintfW(name, fmt, dwIndex);
1166         info = msi_alloc(sizeof(media_info));
1167         if (!info)
1168         {
1169             rc = ERROR_OUTOFMEMORY;
1170             goto done;
1171         }
1172
1173         info->path = strdupW(source);
1174         lstrcpyW(info->szIndex, name);
1175         info->index = dwIndex;
1176         add_source_to_list(&sourcelist, info, &index);
1177
1178         LIST_FOR_EACH_ENTRY(info, &sourcelist, media_info, entry)
1179         {
1180             if (info->index < index)
1181                 continue;
1182
1183             size = (lstrlenW(info->path) + 1) * sizeof(WCHAR);
1184             rc = RegSetValueExW(typekey, info->szIndex, 0,
1185                                 REG_EXPAND_SZ, (LPBYTE)info->path, size);
1186             if (rc != ERROR_SUCCESS)
1187                 goto done;
1188         }
1189     }
1190
1191 done:
1192     free_source_list(&sourcelist);
1193     msi_free(source);
1194     RegCloseKey(typekey);
1195     RegCloseKey(sourcekey);
1196     return rc;
1197 }
1198
1199 /******************************************************************
1200  *  MsiSourceListAddMediaDiskA (MSI.@)
1201  */
1202 UINT WINAPI MsiSourceListAddMediaDiskA(LPCSTR szProduct, LPCSTR szUserSid,
1203         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId,
1204         LPCSTR szVolumeLabel, LPCSTR szDiskPrompt)
1205 {
1206     UINT r;
1207     LPWSTR product = NULL;
1208     LPWSTR usersid = NULL;
1209     LPWSTR volume = NULL;
1210     LPWSTR prompt = NULL;
1211
1212     if (szProduct) product = strdupAtoW(szProduct);
1213     if (szUserSid) usersid = strdupAtoW(szUserSid);
1214     if (szVolumeLabel) volume = strdupAtoW(szVolumeLabel);
1215     if (szDiskPrompt) prompt = strdupAtoW(szDiskPrompt);
1216
1217     r = MsiSourceListAddMediaDiskW(product, usersid, dwContext, dwOptions,
1218                                      dwDiskId, volume, prompt);
1219
1220     msi_free(product);
1221     msi_free(usersid);
1222     msi_free(volume);
1223     msi_free(prompt);
1224
1225     return r;
1226 }
1227
1228 /******************************************************************
1229  *  MsiSourceListAddMediaDiskW (MSI.@)
1230  */
1231 UINT WINAPI MsiSourceListAddMediaDiskW(LPCWSTR szProduct, LPCWSTR szUserSid, 
1232         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId, 
1233         LPCWSTR szVolumeLabel, LPCWSTR szDiskPrompt)
1234 {
1235     HKEY sourcekey;
1236     HKEY mediakey;
1237     UINT rc;
1238     WCHAR szIndex[10];
1239     WCHAR squished_pc[GUID_SIZE];
1240     LPWSTR buffer;
1241     DWORD size;
1242
1243     static const WCHAR fmt[] = {'%','i',0};
1244
1245     TRACE("%s %s %x %x %i %s %s\n", debugstr_w(szProduct),
1246             debugstr_w(szUserSid), dwContext, dwOptions, dwDiskId,
1247             debugstr_w(szVolumeLabel), debugstr_w(szDiskPrompt));
1248
1249     if (!szProduct || !squash_guid(szProduct, squished_pc))
1250         return ERROR_INVALID_PARAMETER;
1251
1252     if (dwOptions != MSICODE_PRODUCT && dwOptions != MSICODE_PATCH)
1253         return ERROR_INVALID_PARAMETER;
1254
1255     if ((szVolumeLabel && !*szVolumeLabel) || (szDiskPrompt && !*szDiskPrompt))
1256         return ERROR_INVALID_PARAMETER;
1257
1258     if ((dwContext & MSIINSTALLCONTEXT_MACHINE) && szUserSid)
1259         return ERROR_INVALID_PARAMETER;
1260
1261     if (dwOptions & MSICODE_PATCH)
1262     {
1263         FIXME("Unhandled options MSICODE_PATCH\n");
1264         return ERROR_FUNCTION_FAILED;
1265     }
1266
1267     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
1268     if (rc != ERROR_SUCCESS)
1269         return rc;
1270
1271     OpenMediaSubkey(sourcekey, &mediakey, TRUE);
1272
1273     sprintfW(szIndex, fmt, dwDiskId);
1274
1275     size = 2;
1276     if (szVolumeLabel) size += lstrlenW(szVolumeLabel);
1277     if (szDiskPrompt) size += lstrlenW(szDiskPrompt);
1278
1279     size *= sizeof(WCHAR);
1280     buffer = msi_alloc(size);
1281     *buffer = '\0';
1282
1283     if (szVolumeLabel) lstrcpyW(buffer, szVolumeLabel);
1284     lstrcatW(buffer, szSemiColon);
1285     if (szDiskPrompt) lstrcatW(buffer, szDiskPrompt);
1286
1287     RegSetValueExW(mediakey, szIndex, 0, REG_SZ, (LPBYTE)buffer, size);
1288     msi_free(buffer);
1289
1290     RegCloseKey(sourcekey);
1291     RegCloseKey(mediakey);
1292
1293     return ERROR_SUCCESS;
1294 }
1295
1296 /******************************************************************
1297  *  MsiSourceListClearAllA (MSI.@)
1298  */
1299 UINT WINAPI MsiSourceListClearAllA( LPCSTR szProduct, LPCSTR szUserName, DWORD dwReserved )
1300 {
1301     FIXME("(%s %s %d)\n", debugstr_a(szProduct), debugstr_a(szUserName), dwReserved);
1302     return ERROR_SUCCESS;
1303 }
1304
1305 /******************************************************************
1306  *  MsiSourceListClearAllW (MSI.@)
1307  */
1308 UINT WINAPI MsiSourceListClearAllW( LPCWSTR szProduct, LPCWSTR szUserName, DWORD dwReserved )
1309 {
1310     FIXME("(%s %s %d)\n", debugstr_w(szProduct), debugstr_w(szUserName), dwReserved);
1311     return ERROR_SUCCESS;
1312 }
1313
1314 /******************************************************************
1315  *  MsiSourceListClearAllExA (MSI.@)
1316  */
1317 UINT WINAPI MsiSourceListClearAllExA( LPCSTR szProduct, LPCSTR szUserSid,
1318     MSIINSTALLCONTEXT dwContext, DWORD dwOptions )
1319 {
1320     FIXME("(%s %s %d %08x)\n", debugstr_a(szProduct), debugstr_a(szUserSid),
1321           dwContext, dwOptions);
1322     return ERROR_SUCCESS;
1323 }
1324
1325 /******************************************************************
1326  *  MsiSourceListClearAllExW (MSI.@)
1327  */
1328 UINT WINAPI MsiSourceListClearAllExW( LPCWSTR szProduct, LPCWSTR szUserSid,
1329     MSIINSTALLCONTEXT dwContext, DWORD dwOptions )
1330 {
1331     FIXME("(%s %s %d %08x)\n", debugstr_w(szProduct), debugstr_w(szUserSid),
1332           dwContext, dwOptions);
1333     return ERROR_SUCCESS;
1334 }
1335
1336 /******************************************************************
1337  *  MsiSourceListClearSourceA (MSI.@)
1338  */
1339 UINT WINAPI MsiSourceListClearSourceA(LPCSTR szProductCodeOrPatchCode, LPCSTR szUserSid,
1340                                       MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
1341                                       LPCSTR szSource)
1342 {
1343     FIXME("(%s %s %x %x %s)\n", debugstr_a(szProductCodeOrPatchCode), debugstr_a(szUserSid),
1344           dwContext, dwOptions, debugstr_a(szSource));
1345     return ERROR_SUCCESS;
1346 }
1347
1348 /******************************************************************
1349  *  MsiSourceListClearSourceW (MSI.@)
1350  */
1351 UINT WINAPI MsiSourceListClearSourceW(LPCWSTR szProductCodeOrPatchCode, LPCWSTR szUserSid,
1352                                       MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
1353                                       LPCWSTR szSource)
1354 {
1355     FIXME("(%s %s %x %x %s)\n", debugstr_w(szProductCodeOrPatchCode), debugstr_w(szUserSid),
1356           dwContext, dwOptions, debugstr_w(szSource));
1357     return ERROR_SUCCESS;
1358 }