msi: Implement and test MsiEnumComponentCostsA/W.
[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         if (size >= *pcchDiskPrompt)
334             r = ERROR_MORE_DATA;
335         else if (szDiskPrompt)
336             lstrcpyW(szDiskPrompt, ptr);
337
338         *pcchDiskPrompt = size;
339     }
340
341     index++;
342
343 done:
344     msi_free(value);
345     msi_free(data);
346     RegCloseKey(source);
347
348     return r;
349 }
350
351 /******************************************************************
352  *  MsiSourceListEnumSourcesA   (MSI.@)
353  */
354 UINT WINAPI MsiSourceListEnumSourcesA(LPCSTR szProductCodeOrPatch, LPCSTR szUserSid,
355                                       MSIINSTALLCONTEXT dwContext,
356                                       DWORD dwOptions, DWORD dwIndex,
357                                       LPSTR szSource, LPDWORD pcchSource)
358 {
359     LPWSTR product = NULL;
360     LPWSTR usersid = NULL;
361     LPWSTR source = NULL;
362     DWORD len = 0;
363     UINT r = ERROR_INVALID_PARAMETER;
364     static DWORD index = 0;
365
366     TRACE("(%s, %s, %d, %d, %d, %p, %p)\n", debugstr_a(szProductCodeOrPatch),
367           debugstr_a(szUserSid), dwContext, dwOptions, dwIndex, szSource, pcchSource);
368
369     if (dwIndex == 0)
370         index = 0;
371
372     if (szSource && !pcchSource)
373         goto done;
374
375     if (dwIndex != index)
376         goto done;
377
378     if (szProductCodeOrPatch) product = strdupAtoW(szProductCodeOrPatch);
379     if (szUserSid) usersid = strdupAtoW(szUserSid);
380
381     r = MsiSourceListEnumSourcesW(product, usersid, dwContext, dwOptions,
382                                   dwIndex, NULL, &len);
383     if (r != ERROR_SUCCESS)
384         goto done;
385
386     source = msi_alloc(++len * sizeof(WCHAR));
387     if (!source)
388     {
389         r = ERROR_OUTOFMEMORY;
390         goto done;
391     }
392
393     *source = '\0';
394     r = MsiSourceListEnumSourcesW(product, usersid, dwContext, dwOptions,
395                                   dwIndex, source, &len);
396     if (r != ERROR_SUCCESS)
397         goto done;
398
399     len = WideCharToMultiByte(CP_ACP, 0, source, -1, NULL, 0, NULL, NULL);
400     if (pcchSource && *pcchSource >= len)
401         WideCharToMultiByte(CP_ACP, 0, source, -1, szSource, len, NULL, NULL);
402     else if (szSource)
403         r = ERROR_MORE_DATA;
404
405     if (pcchSource)
406         *pcchSource = len - 1;
407
408 done:
409     msi_free(product);
410     msi_free(usersid);
411     msi_free(source);
412
413     if (r == ERROR_SUCCESS)
414     {
415         if (szSource || !pcchSource) index++;
416     }
417     else if (dwIndex > index)
418         index = 0;
419
420     return r;
421 }
422
423 /******************************************************************
424  *  MsiSourceListEnumSourcesW   (MSI.@)
425  */
426 UINT WINAPI MsiSourceListEnumSourcesW(LPCWSTR szProductCodeOrPatch, LPCWSTR szUserSid,
427                                       MSIINSTALLCONTEXT dwContext,
428                                       DWORD dwOptions, DWORD dwIndex,
429                                       LPWSTR szSource, LPDWORD pcchSource)
430 {
431     WCHAR squished_pc[GUID_SIZE];
432     WCHAR name[32];
433     HKEY source = NULL;
434     HKEY subkey = NULL;
435     LONG res;
436     UINT r = ERROR_INVALID_PARAMETER;
437     static DWORD index = 0;
438
439     static const WCHAR format[] = {'%','d',0};
440
441     TRACE("(%s, %s, %d, %d, %d, %p, %p)\n", debugstr_w(szProductCodeOrPatch),
442           debugstr_w(szUserSid), dwContext, dwOptions, dwIndex, szSource, pcchSource);
443
444     if (dwIndex == 0)
445         index = 0;
446
447     if (!szProductCodeOrPatch || !squash_guid(szProductCodeOrPatch, squished_pc))
448         goto done;
449
450     if (szSource && !pcchSource)
451         goto done;
452
453     if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
454         goto done;
455
456     if ((dwOptions & MSISOURCETYPE_NETWORK) && (dwOptions & MSISOURCETYPE_URL))
457         goto done;
458
459     if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid)
460         goto done;
461
462     if (dwIndex != index)
463         goto done;
464
465     r = OpenSourceKey(szProductCodeOrPatch, &source,
466                       dwOptions, dwContext, FALSE);
467     if (r != ERROR_SUCCESS)
468         goto done;
469
470     if (dwOptions & MSISOURCETYPE_NETWORK)
471         r = OpenNetworkSubkey(source, &subkey, FALSE);
472     else if (dwOptions & MSISOURCETYPE_URL)
473         r = OpenURLSubkey(source, &subkey, FALSE);
474
475     if (r != ERROR_SUCCESS)
476     {
477         r = ERROR_NO_MORE_ITEMS;
478         goto done;
479     }
480
481     sprintfW(name, format, dwIndex + 1);
482
483     res = RegQueryValueExW(subkey, name, 0, 0, (LPBYTE)szSource, pcchSource);
484     if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA)
485         r = ERROR_NO_MORE_ITEMS;
486
487 done:
488     RegCloseKey(subkey);
489     RegCloseKey(source);
490
491     if (r == ERROR_SUCCESS)
492     {
493         if (szSource || !pcchSource) index++;
494     }
495     else if (dwIndex > index)
496         index = 0;
497
498     return r;
499 }
500
501 /******************************************************************
502  *  MsiSourceListGetInfoA   (MSI.@)
503  */
504 UINT WINAPI MsiSourceListGetInfoA( LPCSTR szProduct, LPCSTR szUserSid,
505                                    MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
506                                    LPCSTR szProperty, LPSTR szValue,
507                                    LPDWORD pcchValue)
508 {
509     UINT ret;
510     LPWSTR product = NULL;
511     LPWSTR usersid = NULL;
512     LPWSTR property = NULL;
513     LPWSTR value = NULL;
514     DWORD len = 0;
515
516     if (szValue && !pcchValue)
517         return ERROR_INVALID_PARAMETER;
518
519     if (szProduct) product = strdupAtoW(szProduct);
520     if (szUserSid) usersid = strdupAtoW(szUserSid);
521     if (szProperty) property = strdupAtoW(szProperty);
522
523     ret = MsiSourceListGetInfoW(product, usersid, dwContext, dwOptions,
524                                 property, NULL, &len);
525     if (ret != ERROR_SUCCESS)
526         goto done;
527
528     value = msi_alloc(++len * sizeof(WCHAR));
529     if (!value)
530         return ERROR_OUTOFMEMORY;
531
532     *value = '\0';
533     ret = MsiSourceListGetInfoW(product, usersid, dwContext, dwOptions,
534                                 property, value, &len);
535     if (ret != ERROR_SUCCESS)
536         goto done;
537
538     len = WideCharToMultiByte(CP_ACP, 0, value, -1, NULL, 0, NULL, NULL);
539     if (*pcchValue >= len)
540         WideCharToMultiByte(CP_ACP, 0, value, -1, szValue, len, NULL, NULL);
541     else if (szValue)
542         ret = ERROR_MORE_DATA;
543
544     *pcchValue = len - 1;
545
546 done:
547     msi_free(product);
548     msi_free(usersid);
549     msi_free(property);
550     msi_free(value);
551     return ret;
552 }
553
554 /******************************************************************
555  *  MsiSourceListGetInfoW   (MSI.@)
556  */
557 UINT WINAPI MsiSourceListGetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
558                                    MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
559                                    LPCWSTR szProperty, LPWSTR szValue, 
560                                    LPDWORD pcchValue) 
561 {
562     WCHAR squished_pc[GUID_SIZE];
563     HKEY sourcekey, media;
564     LPWSTR source, ptr;
565     DWORD size;
566     UINT rc;
567
568     static const WCHAR mediapack[] = {
569         'M','e','d','i','a','P','a','c','k','a','g','e',0};
570
571     TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szProperty));
572
573     if (!szProduct || !squash_guid(szProduct, squished_pc))
574         return ERROR_INVALID_PARAMETER;
575
576     if (szValue && !pcchValue)
577         return ERROR_INVALID_PARAMETER;
578
579     if (dwContext != MSIINSTALLCONTEXT_USERMANAGED &&
580         dwContext != MSIINSTALLCONTEXT_USERUNMANAGED &&
581         dwContext != MSIINSTALLCONTEXT_MACHINE)
582         return ERROR_INVALID_PARAMETER;
583
584     if (!szProperty)
585         return ERROR_INVALID_PARAMETER;
586
587     if (szUserSid)
588         FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
589
590     rc = OpenSourceKey(szProduct, &sourcekey, dwOptions, dwContext, FALSE);
591     if (rc != ERROR_SUCCESS)
592         return rc;
593
594     if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW ) ||
595         !strcmpW( szProperty, INSTALLPROPERTY_DISKPROMPTW ))
596     {
597         rc = OpenMediaSubkey(sourcekey, &media, FALSE);
598         if (rc != ERROR_SUCCESS)
599         {
600             RegCloseKey(sourcekey);
601             return ERROR_SUCCESS;
602         }
603
604         if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW ))
605             szProperty = mediapack;
606
607         RegQueryValueExW(media, szProperty, 0, 0, (LPBYTE)szValue, pcchValue);
608         RegCloseKey(media);
609     }
610     else if (!strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW ) ||
611              !strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDTYPEW ))
612     {
613         rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
614                               0, 0, NULL, &size);
615         if (rc != ERROR_SUCCESS)
616         {
617             RegCloseKey(sourcekey);
618             return ERROR_SUCCESS;
619         }
620
621         source = msi_alloc(size);
622         RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
623                          0, 0, (LPBYTE)source, &size);
624
625         if (!*source)
626         {
627             msi_free(source);
628             RegCloseKey(sourcekey);
629             return ERROR_SUCCESS;
630         }
631
632         if (!strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDTYPEW ))
633         {
634             if (*source != 'n' && *source != 'u' && *source != 'm')
635             {
636                 msi_free(source);
637                 RegCloseKey(sourcekey);
638                 return ERROR_SUCCESS;
639             }
640
641             ptr = source;
642             source[1] = '\0';
643         }
644         else
645         {
646             ptr = strrchrW(source, ';');
647             if (!ptr)
648                 ptr = source;
649             else
650                 ptr++;
651         }
652
653         if (szValue)
654         {
655             if (strlenW(ptr) < *pcchValue)
656                 lstrcpyW(szValue, ptr);
657             else
658                 rc = ERROR_MORE_DATA;
659         }
660
661         *pcchValue = lstrlenW(ptr);
662         msi_free(source);
663     }
664     else if (!strcmpW( szProperty, INSTALLPROPERTY_PACKAGENAMEW ))
665     {
666         *pcchValue = *pcchValue * sizeof(WCHAR);
667         rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, 0,
668                               (LPBYTE)szValue, pcchValue);
669         if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
670         {
671             *pcchValue = 0;
672             rc = ERROR_SUCCESS;
673         }
674         else
675         {
676             if (*pcchValue)
677                 *pcchValue = (*pcchValue - 1) / sizeof(WCHAR);
678             if (szValue)
679                 szValue[*pcchValue] = '\0';
680         }
681     }
682     else
683     {
684         FIXME("Unknown property %s\n",debugstr_w(szProperty));
685         rc = ERROR_UNKNOWN_PROPERTY;
686     }
687
688     RegCloseKey(sourcekey);
689     return rc;
690 }
691
692 /******************************************************************
693  *  MsiSourceListSetInfoA   (MSI.@)
694  */
695 UINT WINAPI MsiSourceListSetInfoA(LPCSTR szProduct, LPCSTR szUserSid,
696                                   MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
697                                   LPCSTR szProperty, LPCSTR szValue)
698 {
699     UINT ret;
700     LPWSTR product = NULL;
701     LPWSTR usersid = NULL;
702     LPWSTR property = NULL;
703     LPWSTR value = NULL;
704
705     if (szProduct) product = strdupAtoW(szProduct);
706     if (szUserSid) usersid = strdupAtoW(szUserSid);
707     if (szProperty) property = strdupAtoW(szProperty);
708     if (szValue) value = strdupAtoW(szValue);
709
710     ret = MsiSourceListSetInfoW(product, usersid, dwContext, dwOptions,
711                                 property, value);
712
713     msi_free(product);
714     msi_free(usersid);
715     msi_free(property);
716     msi_free(value);
717
718     return ret;
719 }
720
721 UINT msi_set_last_used_source(LPCWSTR product, LPCWSTR usersid,
722                               MSIINSTALLCONTEXT context, DWORD options,
723                               LPCWSTR value)
724 {
725     HKEY source;
726     LPWSTR buffer;
727     WCHAR typechar;
728     DWORD size;
729     UINT r;
730     int index = 1;
731
732     static const WCHAR format[] = {'%','c',';','%','i',';','%','s',0};
733
734     if (options & MSISOURCETYPE_NETWORK)
735         typechar = 'n';
736     else if (options & MSISOURCETYPE_URL)
737         typechar = 'u';
738     else if (options & MSISOURCETYPE_MEDIA)
739         typechar = 'm';
740     else
741         return ERROR_INVALID_PARAMETER;
742
743     if (!(options & MSISOURCETYPE_MEDIA))
744     {
745         r = MsiSourceListAddSourceExW(product, usersid, context,
746                                       options, value, 0);
747         if (r != ERROR_SUCCESS)
748             return r;
749
750         index = 0;
751         while ((r = MsiSourceListEnumSourcesW(product, usersid, context, options,
752                                               index, NULL, NULL)) == ERROR_SUCCESS)
753             index++;
754
755         if (r != ERROR_NO_MORE_ITEMS)
756             return r;
757     }
758
759     size = (lstrlenW(format) + lstrlenW(value) + 7) * sizeof(WCHAR);
760     buffer = msi_alloc(size);
761     if (!buffer)
762         return ERROR_OUTOFMEMORY;
763
764     r = OpenSourceKey(product, &source, MSICODE_PRODUCT, context, FALSE);
765     if (r != ERROR_SUCCESS)
766         return r;
767
768     sprintfW(buffer, format, typechar, index, value);
769
770     size = (lstrlenW(buffer) + 1) * sizeof(WCHAR);
771     r = RegSetValueExW(source, INSTALLPROPERTY_LASTUSEDSOURCEW, 0,
772                        REG_SZ, (LPBYTE)buffer, size);
773     msi_free(buffer);
774
775     RegCloseKey(source);
776     return r;
777 }
778
779 /******************************************************************
780  *  MsiSourceListSetInfoW   (MSI.@)
781  */
782 UINT WINAPI MsiSourceListSetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
783                                    MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
784                                    LPCWSTR szProperty, LPCWSTR szValue)
785 {
786     WCHAR squished_pc[GUID_SIZE];
787     HKEY sourcekey, media;
788     LPCWSTR property;
789     UINT rc;
790
791     static const WCHAR media_package[] = {
792         'M','e','d','i','a','P','a','c','k','a','g','e',0
793     };
794
795     TRACE("%s %s %x %x %s %s\n", debugstr_w(szProduct), debugstr_w(szUserSid),
796             dwContext, dwOptions, debugstr_w(szProperty), debugstr_w(szValue));
797
798     if (!szProduct || !squash_guid(szProduct, squished_pc))
799         return ERROR_INVALID_PARAMETER;
800
801     if (!szProperty)
802         return ERROR_INVALID_PARAMETER;
803
804     if (!szValue)
805         return ERROR_UNKNOWN_PROPERTY;
806
807     if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid)
808         return ERROR_INVALID_PARAMETER;
809
810     if (dwOptions & MSICODE_PATCH)
811     {
812         FIXME("Unhandled options MSICODE_PATCH\n");
813         return ERROR_UNKNOWN_PATCH;
814     }
815
816     property = szProperty;
817     if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW ))
818         property = media_package;
819
820     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
821     if (rc != ERROR_SUCCESS)
822         return rc;
823
824     if (strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW ) &&
825         dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL))
826     {
827         RegCloseKey(sourcekey);
828         return ERROR_INVALID_PARAMETER;
829     }
830
831     if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW ) ||
832         !strcmpW( szProperty, INSTALLPROPERTY_DISKPROMPTW ))
833     {
834         rc = OpenMediaSubkey(sourcekey, &media, TRUE);
835         if (rc == ERROR_SUCCESS)
836         {
837             rc = msi_reg_set_val_str(media, property, szValue);
838             RegCloseKey(media);
839         }
840     }
841     else if (!strcmpW( szProperty, INSTALLPROPERTY_PACKAGENAMEW ))
842     {
843         DWORD size = (lstrlenW(szValue) + 1) * sizeof(WCHAR);
844         rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0,
845                 REG_SZ, (const BYTE *)szValue, size);
846         if (rc != ERROR_SUCCESS)
847             rc = ERROR_UNKNOWN_PROPERTY;
848     }
849     else if (!strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW ))
850     {
851         if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
852             rc = ERROR_INVALID_PARAMETER;
853         else
854             rc = msi_set_last_used_source(szProduct, szUserSid, dwContext,
855                                           dwOptions, szValue);
856     }
857     else
858         rc = ERROR_UNKNOWN_PROPERTY;
859
860     RegCloseKey(sourcekey);
861     return rc;
862 }
863
864 /******************************************************************
865  *  MsiSourceListAddSourceW (MSI.@)
866  */
867 UINT WINAPI MsiSourceListAddSourceW( LPCWSTR szProduct, LPCWSTR szUserName,
868         DWORD dwReserved, LPCWSTR szSource)
869 {
870     WCHAR squished_pc[GUID_SIZE];
871     INT ret;
872     LPWSTR sidstr = NULL;
873     DWORD sidsize = 0;
874     DWORD domsize = 0;
875     DWORD context;
876     HKEY hkey = 0;
877     UINT r;
878
879     TRACE("%s %s %s\n", debugstr_w(szProduct), debugstr_w(szUserName), debugstr_w(szSource));
880
881     if (!szSource || !*szSource)
882         return ERROR_INVALID_PARAMETER;
883
884     if (dwReserved != 0)
885         return ERROR_INVALID_PARAMETER;
886
887     if (!szProduct || !squash_guid(szProduct, squished_pc))
888         return ERROR_INVALID_PARAMETER;
889
890     if (!szUserName || !*szUserName)
891         context = MSIINSTALLCONTEXT_MACHINE;
892     else
893     {
894         if (LookupAccountNameW(NULL, szUserName, NULL, &sidsize, NULL, &domsize, NULL))
895         {
896             PSID psid = msi_alloc(sidsize);
897
898             if (LookupAccountNameW(NULL, szUserName, psid, &sidsize, NULL, &domsize, NULL))
899                 ConvertSidToStringSidW(psid, &sidstr);
900
901             msi_free(psid);
902         }
903
904         r = MSIREG_OpenProductKey(szProduct, NULL,
905                                   MSIINSTALLCONTEXT_USERMANAGED, &hkey, FALSE);
906         if (r == ERROR_SUCCESS)
907             context = MSIINSTALLCONTEXT_USERMANAGED;
908         else
909         {
910             r = MSIREG_OpenProductKey(szProduct, NULL,
911                                       MSIINSTALLCONTEXT_USERUNMANAGED,
912                                       &hkey, FALSE);
913             if (r != ERROR_SUCCESS)
914                 return ERROR_UNKNOWN_PRODUCT;
915
916             context = MSIINSTALLCONTEXT_USERUNMANAGED;
917         }
918
919         RegCloseKey(hkey);
920     }
921
922     ret = MsiSourceListAddSourceExW(szProduct, sidstr, 
923         context, MSISOURCETYPE_NETWORK, szSource, 0);
924
925     if (sidstr)
926         LocalFree(sidstr);
927
928     return ret;
929 }
930
931 /******************************************************************
932  *  MsiSourceListAddSourceA (MSI.@)
933  */
934 UINT WINAPI MsiSourceListAddSourceA( LPCSTR szProduct, LPCSTR szUserName,
935         DWORD dwReserved, LPCSTR szSource)
936 {
937     INT ret;
938     LPWSTR szwproduct;
939     LPWSTR szwusername;
940     LPWSTR szwsource;
941
942     szwproduct = strdupAtoW( szProduct );
943     szwusername = strdupAtoW( szUserName );
944     szwsource = strdupAtoW( szSource );
945
946     ret = MsiSourceListAddSourceW(szwproduct, szwusername, dwReserved, szwsource);
947
948     msi_free(szwproduct);
949     msi_free(szwusername);
950     msi_free(szwsource);
951
952     return ret;
953 }
954
955 /******************************************************************
956  *  MsiSourceListAddSourceExA (MSI.@)
957  */
958 UINT WINAPI MsiSourceListAddSourceExA(LPCSTR szProduct, LPCSTR szUserSid,
959         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCSTR szSource, DWORD dwIndex)
960 {
961     UINT ret;
962     LPWSTR product, usersid, source;
963
964     product = strdupAtoW(szProduct);
965     usersid = strdupAtoW(szUserSid);
966     source = strdupAtoW(szSource);
967
968     ret = MsiSourceListAddSourceExW(product, usersid, dwContext,
969                                     dwOptions, source, dwIndex);
970
971     msi_free(product);
972     msi_free(usersid);
973     msi_free(source);
974
975     return ret;
976 }
977
978 static void free_source_list(struct list *sourcelist)
979 {
980     while (!list_empty(sourcelist))
981     {
982         media_info *info = LIST_ENTRY(list_head(sourcelist), media_info, entry);
983         list_remove(&info->entry);
984         msi_free(info->path);
985         msi_free(info);
986     }
987 }
988
989 static void add_source_to_list(struct list *sourcelist, media_info *info,
990                                DWORD *index)
991 {
992     media_info *iter;
993     BOOL found = FALSE;
994     static const WCHAR fmt[] = {'%','i',0};
995
996     if (index) *index = 0;
997
998     if (list_empty(sourcelist))
999     {
1000         list_add_head(sourcelist, &info->entry);
1001         return;
1002     }
1003
1004     LIST_FOR_EACH_ENTRY(iter, sourcelist, media_info, entry)
1005     {
1006         if (!found && info->index < iter->index)
1007         {
1008             found = TRUE;
1009             list_add_before(&iter->entry, &info->entry);
1010         }
1011
1012         /* update the rest of the list */
1013         if (found)
1014             sprintfW(iter->szIndex, fmt, ++iter->index);
1015         else if (index)
1016             (*index)++;
1017     }
1018
1019     if (!found)
1020         list_add_after(&iter->entry, &info->entry);
1021 }
1022
1023 static UINT fill_source_list(struct list *sourcelist, HKEY sourcekey, DWORD *count)
1024 {
1025     UINT r = ERROR_SUCCESS;
1026     DWORD index = 0;
1027     WCHAR name[10];
1028     DWORD size, val_size;
1029     media_info *entry;
1030
1031     *count = 0;
1032
1033     while (r == ERROR_SUCCESS)
1034     {
1035         size = sizeof(name) / sizeof(name[0]);
1036         r = RegEnumValueW(sourcekey, index, name, &size, NULL, NULL, NULL, &val_size);
1037         if (r != ERROR_SUCCESS)
1038             return r;
1039
1040         entry = msi_alloc(sizeof(media_info));
1041         if (!entry)
1042             goto error;
1043
1044         entry->path = msi_alloc(val_size);
1045         if (!entry->path)
1046         {
1047             msi_free(entry);
1048             goto error;
1049         }
1050
1051         lstrcpyW(entry->szIndex, name);
1052         entry->index = atoiW(name);
1053
1054         size++;
1055         r = RegEnumValueW(sourcekey, index, name, &size, NULL,
1056                           NULL, (LPBYTE)entry->path, &val_size);
1057         if (r != ERROR_SUCCESS)
1058         {
1059             msi_free(entry->path);
1060             msi_free(entry);
1061             goto error;
1062         }
1063
1064         index = ++(*count);
1065         add_source_to_list(sourcelist, entry, NULL);
1066     }
1067
1068 error:
1069     *count = -1;
1070     free_source_list(sourcelist);
1071     return ERROR_OUTOFMEMORY;
1072 }
1073
1074 /******************************************************************
1075  *  MsiSourceListAddSourceExW (MSI.@)
1076  */
1077 UINT WINAPI MsiSourceListAddSourceExW( LPCWSTR szProduct, LPCWSTR szUserSid,
1078         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szSource, 
1079         DWORD dwIndex)
1080 {
1081     HKEY sourcekey;
1082     HKEY typekey;
1083     UINT rc;
1084     struct list sourcelist;
1085     media_info *info;
1086     WCHAR squished_pc[GUID_SIZE];
1087     WCHAR name[10];
1088     LPWSTR source;
1089     LPCWSTR postfix;
1090     DWORD size, count;
1091     DWORD index;
1092
1093     static const WCHAR fmt[] = {'%','i',0};
1094
1095     TRACE("%s %s %x %x %s %i\n", debugstr_w(szProduct), debugstr_w(szUserSid),
1096           dwContext, dwOptions, debugstr_w(szSource), dwIndex);
1097
1098     if (!szProduct || !squash_guid(szProduct, squished_pc))
1099         return ERROR_INVALID_PARAMETER;
1100
1101     if (!szSource || !*szSource)
1102         return ERROR_INVALID_PARAMETER;
1103
1104     if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
1105         return ERROR_INVALID_PARAMETER;
1106
1107     if (dwOptions & MSICODE_PATCH)
1108     {
1109         FIXME("Unhandled options MSICODE_PATCH\n");
1110         return ERROR_FUNCTION_FAILED;
1111     }
1112
1113     if (szUserSid && (dwContext & MSIINSTALLCONTEXT_MACHINE))
1114         return ERROR_INVALID_PARAMETER;
1115
1116     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
1117     if (rc != ERROR_SUCCESS)
1118         return rc;
1119
1120     if (dwOptions & MSISOURCETYPE_NETWORK)
1121         rc = OpenNetworkSubkey(sourcekey, &typekey, TRUE);
1122     else if (dwOptions & MSISOURCETYPE_URL)
1123         rc = OpenURLSubkey(sourcekey, &typekey, TRUE);
1124     else if (dwOptions & MSISOURCETYPE_MEDIA)
1125         rc = OpenMediaSubkey(sourcekey, &typekey, TRUE);
1126     else
1127     {
1128         ERR("unknown media type: %08x\n", dwOptions);
1129         RegCloseKey(sourcekey);
1130         return ERROR_FUNCTION_FAILED;
1131     }
1132     if (rc != ERROR_SUCCESS)
1133     {
1134         ERR("can't open subkey %u\n", rc);
1135         RegCloseKey(sourcekey);
1136         return rc;
1137     }
1138
1139     postfix = (dwOptions & MSISOURCETYPE_NETWORK) ? szBackSlash : szForwardSlash;
1140     if (szSource[lstrlenW(szSource) - 1] == *postfix)
1141         source = strdupW(szSource);
1142     else
1143     {
1144         size = lstrlenW(szSource) + 2;
1145         source = msi_alloc(size * sizeof(WCHAR));
1146         lstrcpyW(source, szSource);
1147         lstrcatW(source, postfix);
1148     }
1149
1150     list_init(&sourcelist);
1151     rc = fill_source_list(&sourcelist, typekey, &count);
1152     if (rc != ERROR_NO_MORE_ITEMS)
1153         return rc;
1154
1155     size = (lstrlenW(source) + 1) * sizeof(WCHAR);
1156
1157     if (count == 0)
1158     {
1159         rc = RegSetValueExW(typekey, szOne, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
1160         goto done;
1161     }
1162     else if (dwIndex > count || dwIndex == 0)
1163     {
1164         sprintfW(name, fmt, count + 1);
1165         rc = RegSetValueExW(typekey, name, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
1166         goto done;
1167     }
1168     else
1169     {
1170         sprintfW(name, fmt, dwIndex);
1171         info = msi_alloc(sizeof(media_info));
1172         if (!info)
1173         {
1174             rc = ERROR_OUTOFMEMORY;
1175             goto done;
1176         }
1177
1178         info->path = strdupW(source);
1179         lstrcpyW(info->szIndex, name);
1180         info->index = dwIndex;
1181         add_source_to_list(&sourcelist, info, &index);
1182
1183         LIST_FOR_EACH_ENTRY(info, &sourcelist, media_info, entry)
1184         {
1185             if (info->index < index)
1186                 continue;
1187
1188             size = (lstrlenW(info->path) + 1) * sizeof(WCHAR);
1189             rc = RegSetValueExW(typekey, info->szIndex, 0,
1190                                 REG_EXPAND_SZ, (LPBYTE)info->path, size);
1191             if (rc != ERROR_SUCCESS)
1192                 goto done;
1193         }
1194     }
1195
1196 done:
1197     free_source_list(&sourcelist);
1198     msi_free(source);
1199     RegCloseKey(typekey);
1200     RegCloseKey(sourcekey);
1201     return rc;
1202 }
1203
1204 /******************************************************************
1205  *  MsiSourceListAddMediaDiskA (MSI.@)
1206  */
1207 UINT WINAPI MsiSourceListAddMediaDiskA(LPCSTR szProduct, LPCSTR szUserSid,
1208         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId,
1209         LPCSTR szVolumeLabel, LPCSTR szDiskPrompt)
1210 {
1211     UINT r;
1212     LPWSTR product = NULL;
1213     LPWSTR usersid = NULL;
1214     LPWSTR volume = NULL;
1215     LPWSTR prompt = NULL;
1216
1217     if (szProduct) product = strdupAtoW(szProduct);
1218     if (szUserSid) usersid = strdupAtoW(szUserSid);
1219     if (szVolumeLabel) volume = strdupAtoW(szVolumeLabel);
1220     if (szDiskPrompt) prompt = strdupAtoW(szDiskPrompt);
1221
1222     r = MsiSourceListAddMediaDiskW(product, usersid, dwContext, dwOptions,
1223                                      dwDiskId, volume, prompt);
1224
1225     msi_free(product);
1226     msi_free(usersid);
1227     msi_free(volume);
1228     msi_free(prompt);
1229
1230     return r;
1231 }
1232
1233 /******************************************************************
1234  *  MsiSourceListAddMediaDiskW (MSI.@)
1235  */
1236 UINT WINAPI MsiSourceListAddMediaDiskW(LPCWSTR szProduct, LPCWSTR szUserSid, 
1237         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId, 
1238         LPCWSTR szVolumeLabel, LPCWSTR szDiskPrompt)
1239 {
1240     HKEY sourcekey;
1241     HKEY mediakey;
1242     UINT rc;
1243     WCHAR szIndex[10];
1244     WCHAR squished_pc[GUID_SIZE];
1245     LPWSTR buffer;
1246     DWORD size;
1247
1248     static const WCHAR fmt[] = {'%','i',0};
1249
1250     TRACE("%s %s %x %x %i %s %s\n", debugstr_w(szProduct),
1251             debugstr_w(szUserSid), dwContext, dwOptions, dwDiskId,
1252             debugstr_w(szVolumeLabel), debugstr_w(szDiskPrompt));
1253
1254     if (!szProduct || !squash_guid(szProduct, squished_pc))
1255         return ERROR_INVALID_PARAMETER;
1256
1257     if (dwOptions != MSICODE_PRODUCT && dwOptions != MSICODE_PATCH)
1258         return ERROR_INVALID_PARAMETER;
1259
1260     if ((szVolumeLabel && !*szVolumeLabel) || (szDiskPrompt && !*szDiskPrompt))
1261         return ERROR_INVALID_PARAMETER;
1262
1263     if ((dwContext & MSIINSTALLCONTEXT_MACHINE) && szUserSid)
1264         return ERROR_INVALID_PARAMETER;
1265
1266     if (dwOptions & MSICODE_PATCH)
1267     {
1268         FIXME("Unhandled options MSICODE_PATCH\n");
1269         return ERROR_FUNCTION_FAILED;
1270     }
1271
1272     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
1273     if (rc != ERROR_SUCCESS)
1274         return rc;
1275
1276     OpenMediaSubkey(sourcekey, &mediakey, TRUE);
1277
1278     sprintfW(szIndex, fmt, dwDiskId);
1279
1280     size = 2;
1281     if (szVolumeLabel) size += lstrlenW(szVolumeLabel);
1282     if (szDiskPrompt) size += lstrlenW(szDiskPrompt);
1283
1284     size *= sizeof(WCHAR);
1285     buffer = msi_alloc(size);
1286     *buffer = '\0';
1287
1288     if (szVolumeLabel) lstrcpyW(buffer, szVolumeLabel);
1289     lstrcatW(buffer, szSemiColon);
1290     if (szDiskPrompt) lstrcatW(buffer, szDiskPrompt);
1291
1292     RegSetValueExW(mediakey, szIndex, 0, REG_SZ, (LPBYTE)buffer, size);
1293     msi_free(buffer);
1294
1295     RegCloseKey(sourcekey);
1296     RegCloseKey(mediakey);
1297
1298     return ERROR_SUCCESS;
1299 }
1300
1301 /******************************************************************
1302  *  MsiSourceListClearAllA (MSI.@)
1303  */
1304 UINT WINAPI MsiSourceListClearAllA( LPCSTR szProduct, LPCSTR szUserName, DWORD dwReserved )
1305 {
1306     FIXME("(%s %s %d)\n", debugstr_a(szProduct), debugstr_a(szUserName), dwReserved);
1307     return ERROR_SUCCESS;
1308 }
1309
1310 /******************************************************************
1311  *  MsiSourceListClearAllW (MSI.@)
1312  */
1313 UINT WINAPI MsiSourceListClearAllW( LPCWSTR szProduct, LPCWSTR szUserName, DWORD dwReserved )
1314 {
1315     FIXME("(%s %s %d)\n", debugstr_w(szProduct), debugstr_w(szUserName), dwReserved);
1316     return ERROR_SUCCESS;
1317 }
1318
1319 /******************************************************************
1320  *  MsiSourceListClearAllExA (MSI.@)
1321  */
1322 UINT WINAPI MsiSourceListClearAllExA( LPCSTR szProduct, LPCSTR szUserSid,
1323     MSIINSTALLCONTEXT dwContext, DWORD dwOptions )
1324 {
1325     FIXME("(%s %s %d %08x)\n", debugstr_a(szProduct), debugstr_a(szUserSid),
1326           dwContext, dwOptions);
1327     return ERROR_SUCCESS;
1328 }
1329
1330 /******************************************************************
1331  *  MsiSourceListClearAllExW (MSI.@)
1332  */
1333 UINT WINAPI MsiSourceListClearAllExW( LPCWSTR szProduct, LPCWSTR szUserSid,
1334     MSIINSTALLCONTEXT dwContext, DWORD dwOptions )
1335 {
1336     FIXME("(%s %s %d %08x)\n", debugstr_w(szProduct), debugstr_w(szUserSid),
1337           dwContext, dwOptions);
1338     return ERROR_SUCCESS;
1339 }
1340
1341 /******************************************************************
1342  *  MsiSourceListClearSourceA (MSI.@)
1343  */
1344 UINT WINAPI MsiSourceListClearSourceA(LPCSTR szProductCodeOrPatchCode, LPCSTR szUserSid,
1345                                       MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
1346                                       LPCSTR szSource)
1347 {
1348     FIXME("(%s %s %x %x %s)\n", debugstr_a(szProductCodeOrPatchCode), debugstr_a(szUserSid),
1349           dwContext, dwOptions, debugstr_a(szSource));
1350     return ERROR_SUCCESS;
1351 }
1352
1353 /******************************************************************
1354  *  MsiSourceListClearSourceW (MSI.@)
1355  */
1356 UINT WINAPI MsiSourceListClearSourceW(LPCWSTR szProductCodeOrPatchCode, LPCWSTR szUserSid,
1357                                       MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
1358                                       LPCWSTR szSource)
1359 {
1360     FIXME("(%s %s %x %x %s)\n", debugstr_w(szProductCodeOrPatchCode), debugstr_w(szUserSid),
1361           dwContext, dwOptions, debugstr_w(szSource));
1362     return ERROR_SUCCESS;
1363 }