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