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