atl80: Added AtlComModuleRegisterServer implementation (based on AtlModuleRegisterSer...
[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     {
767         msi_free(buffer);
768         return r;
769     }
770
771     sprintfW(buffer, format, typechar, index, value);
772
773     size = (lstrlenW(buffer) + 1) * sizeof(WCHAR);
774     r = RegSetValueExW(source, INSTALLPROPERTY_LASTUSEDSOURCEW, 0,
775                        REG_SZ, (LPBYTE)buffer, size);
776     msi_free(buffer);
777
778     RegCloseKey(source);
779     return r;
780 }
781
782 /******************************************************************
783  *  MsiSourceListSetInfoW   (MSI.@)
784  */
785 UINT WINAPI MsiSourceListSetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
786                                    MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
787                                    LPCWSTR szProperty, LPCWSTR szValue)
788 {
789     WCHAR squished_pc[GUID_SIZE];
790     HKEY sourcekey, media;
791     LPCWSTR property;
792     UINT rc;
793
794     static const WCHAR media_package[] = {
795         'M','e','d','i','a','P','a','c','k','a','g','e',0
796     };
797
798     TRACE("%s %s %x %x %s %s\n", debugstr_w(szProduct), debugstr_w(szUserSid),
799             dwContext, dwOptions, debugstr_w(szProperty), debugstr_w(szValue));
800
801     if (!szProduct || !squash_guid(szProduct, squished_pc))
802         return ERROR_INVALID_PARAMETER;
803
804     if (!szProperty)
805         return ERROR_INVALID_PARAMETER;
806
807     if (!szValue)
808         return ERROR_UNKNOWN_PROPERTY;
809
810     if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid)
811         return ERROR_INVALID_PARAMETER;
812
813     if (dwOptions & MSICODE_PATCH)
814     {
815         FIXME("Unhandled options MSICODE_PATCH\n");
816         return ERROR_UNKNOWN_PATCH;
817     }
818
819     property = szProperty;
820     if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW ))
821         property = media_package;
822
823     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
824     if (rc != ERROR_SUCCESS)
825         return rc;
826
827     if (strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW ) &&
828         dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL))
829     {
830         RegCloseKey(sourcekey);
831         return ERROR_INVALID_PARAMETER;
832     }
833
834     if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW ) ||
835         !strcmpW( szProperty, INSTALLPROPERTY_DISKPROMPTW ))
836     {
837         rc = OpenMediaSubkey(sourcekey, &media, TRUE);
838         if (rc == ERROR_SUCCESS)
839         {
840             rc = msi_reg_set_val_str(media, property, szValue);
841             RegCloseKey(media);
842         }
843     }
844     else if (!strcmpW( szProperty, INSTALLPROPERTY_PACKAGENAMEW ))
845     {
846         DWORD size = (lstrlenW(szValue) + 1) * sizeof(WCHAR);
847         rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0,
848                 REG_SZ, (const BYTE *)szValue, size);
849         if (rc != ERROR_SUCCESS)
850             rc = ERROR_UNKNOWN_PROPERTY;
851     }
852     else if (!strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW ))
853     {
854         if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
855             rc = ERROR_INVALID_PARAMETER;
856         else
857             rc = msi_set_last_used_source(szProduct, szUserSid, dwContext,
858                                           dwOptions, szValue);
859     }
860     else
861         rc = ERROR_UNKNOWN_PROPERTY;
862
863     RegCloseKey(sourcekey);
864     return rc;
865 }
866
867 /******************************************************************
868  *  MsiSourceListAddSourceW (MSI.@)
869  */
870 UINT WINAPI MsiSourceListAddSourceW( LPCWSTR szProduct, LPCWSTR szUserName,
871         DWORD dwReserved, LPCWSTR szSource)
872 {
873     WCHAR squished_pc[GUID_SIZE];
874     INT ret;
875     LPWSTR sidstr = NULL;
876     DWORD sidsize = 0;
877     DWORD domsize = 0;
878     DWORD context;
879     HKEY hkey = 0;
880     UINT r;
881
882     TRACE("%s %s %s\n", debugstr_w(szProduct), debugstr_w(szUserName), debugstr_w(szSource));
883
884     if (!szSource || !*szSource)
885         return ERROR_INVALID_PARAMETER;
886
887     if (dwReserved != 0)
888         return ERROR_INVALID_PARAMETER;
889
890     if (!szProduct || !squash_guid(szProduct, squished_pc))
891         return ERROR_INVALID_PARAMETER;
892
893     if (!szUserName || !*szUserName)
894         context = MSIINSTALLCONTEXT_MACHINE;
895     else
896     {
897         if (LookupAccountNameW(NULL, szUserName, NULL, &sidsize, NULL, &domsize, NULL))
898         {
899             PSID psid = msi_alloc(sidsize);
900
901             if (LookupAccountNameW(NULL, szUserName, psid, &sidsize, NULL, &domsize, NULL))
902                 ConvertSidToStringSidW(psid, &sidstr);
903
904             msi_free(psid);
905         }
906
907         r = MSIREG_OpenProductKey(szProduct, NULL,
908                                   MSIINSTALLCONTEXT_USERMANAGED, &hkey, FALSE);
909         if (r == ERROR_SUCCESS)
910             context = MSIINSTALLCONTEXT_USERMANAGED;
911         else
912         {
913             r = MSIREG_OpenProductKey(szProduct, NULL,
914                                       MSIINSTALLCONTEXT_USERUNMANAGED,
915                                       &hkey, FALSE);
916             if (r != ERROR_SUCCESS)
917                 return ERROR_UNKNOWN_PRODUCT;
918
919             context = MSIINSTALLCONTEXT_USERUNMANAGED;
920         }
921
922         RegCloseKey(hkey);
923     }
924
925     ret = MsiSourceListAddSourceExW(szProduct, sidstr, 
926         context, MSISOURCETYPE_NETWORK, szSource, 0);
927
928     if (sidstr)
929         LocalFree(sidstr);
930
931     return ret;
932 }
933
934 /******************************************************************
935  *  MsiSourceListAddSourceA (MSI.@)
936  */
937 UINT WINAPI MsiSourceListAddSourceA( LPCSTR szProduct, LPCSTR szUserName,
938         DWORD dwReserved, LPCSTR szSource)
939 {
940     INT ret;
941     LPWSTR szwproduct;
942     LPWSTR szwusername;
943     LPWSTR szwsource;
944
945     szwproduct = strdupAtoW( szProduct );
946     szwusername = strdupAtoW( szUserName );
947     szwsource = strdupAtoW( szSource );
948
949     ret = MsiSourceListAddSourceW(szwproduct, szwusername, dwReserved, szwsource);
950
951     msi_free(szwproduct);
952     msi_free(szwusername);
953     msi_free(szwsource);
954
955     return ret;
956 }
957
958 /******************************************************************
959  *  MsiSourceListAddSourceExA (MSI.@)
960  */
961 UINT WINAPI MsiSourceListAddSourceExA(LPCSTR szProduct, LPCSTR szUserSid,
962         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCSTR szSource, DWORD dwIndex)
963 {
964     UINT ret;
965     LPWSTR product, usersid, source;
966
967     product = strdupAtoW(szProduct);
968     usersid = strdupAtoW(szUserSid);
969     source = strdupAtoW(szSource);
970
971     ret = MsiSourceListAddSourceExW(product, usersid, dwContext,
972                                     dwOptions, source, dwIndex);
973
974     msi_free(product);
975     msi_free(usersid);
976     msi_free(source);
977
978     return ret;
979 }
980
981 static void free_source_list(struct list *sourcelist)
982 {
983     while (!list_empty(sourcelist))
984     {
985         media_info *info = LIST_ENTRY(list_head(sourcelist), media_info, entry);
986         list_remove(&info->entry);
987         msi_free(info->path);
988         msi_free(info);
989     }
990 }
991
992 static void add_source_to_list(struct list *sourcelist, media_info *info,
993                                DWORD *index)
994 {
995     media_info *iter;
996     BOOL found = FALSE;
997     static const WCHAR fmt[] = {'%','i',0};
998
999     if (index) *index = 0;
1000
1001     if (list_empty(sourcelist))
1002     {
1003         list_add_head(sourcelist, &info->entry);
1004         return;
1005     }
1006
1007     LIST_FOR_EACH_ENTRY(iter, sourcelist, media_info, entry)
1008     {
1009         if (!found && info->index < iter->index)
1010         {
1011             found = TRUE;
1012             list_add_before(&iter->entry, &info->entry);
1013         }
1014
1015         /* update the rest of the list */
1016         if (found)
1017             sprintfW(iter->szIndex, fmt, ++iter->index);
1018         else if (index)
1019             (*index)++;
1020     }
1021
1022     if (!found)
1023         list_add_after(&iter->entry, &info->entry);
1024 }
1025
1026 static UINT fill_source_list(struct list *sourcelist, HKEY sourcekey, DWORD *count)
1027 {
1028     UINT r = ERROR_SUCCESS;
1029     DWORD index = 0;
1030     WCHAR name[10];
1031     DWORD size, val_size;
1032     media_info *entry;
1033
1034     *count = 0;
1035
1036     while (r == ERROR_SUCCESS)
1037     {
1038         size = sizeof(name) / sizeof(name[0]);
1039         r = RegEnumValueW(sourcekey, index, name, &size, NULL, NULL, NULL, &val_size);
1040         if (r != ERROR_SUCCESS)
1041             return r;
1042
1043         entry = msi_alloc(sizeof(media_info));
1044         if (!entry)
1045             goto error;
1046
1047         entry->path = msi_alloc(val_size);
1048         if (!entry->path)
1049         {
1050             msi_free(entry);
1051             goto error;
1052         }
1053
1054         lstrcpyW(entry->szIndex, name);
1055         entry->index = atoiW(name);
1056
1057         size++;
1058         r = RegEnumValueW(sourcekey, index, name, &size, NULL,
1059                           NULL, (LPBYTE)entry->path, &val_size);
1060         if (r != ERROR_SUCCESS)
1061         {
1062             msi_free(entry->path);
1063             msi_free(entry);
1064             goto error;
1065         }
1066
1067         index = ++(*count);
1068         add_source_to_list(sourcelist, entry, NULL);
1069     }
1070
1071 error:
1072     *count = -1;
1073     free_source_list(sourcelist);
1074     return ERROR_OUTOFMEMORY;
1075 }
1076
1077 /******************************************************************
1078  *  MsiSourceListAddSourceExW (MSI.@)
1079  */
1080 UINT WINAPI MsiSourceListAddSourceExW( LPCWSTR szProduct, LPCWSTR szUserSid,
1081         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szSource, 
1082         DWORD dwIndex)
1083 {
1084     HKEY sourcekey;
1085     HKEY typekey;
1086     UINT rc;
1087     struct list sourcelist;
1088     media_info *info;
1089     WCHAR squished_pc[GUID_SIZE];
1090     WCHAR name[10];
1091     LPWSTR source;
1092     LPCWSTR postfix;
1093     DWORD size, count;
1094     DWORD index;
1095
1096     static const WCHAR fmt[] = {'%','i',0};
1097
1098     TRACE("%s %s %x %x %s %i\n", debugstr_w(szProduct), debugstr_w(szUserSid),
1099           dwContext, dwOptions, debugstr_w(szSource), dwIndex);
1100
1101     if (!szProduct || !squash_guid(szProduct, squished_pc))
1102         return ERROR_INVALID_PARAMETER;
1103
1104     if (!szSource || !*szSource)
1105         return ERROR_INVALID_PARAMETER;
1106
1107     if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
1108         return ERROR_INVALID_PARAMETER;
1109
1110     if (dwOptions & MSICODE_PATCH)
1111     {
1112         FIXME("Unhandled options MSICODE_PATCH\n");
1113         return ERROR_FUNCTION_FAILED;
1114     }
1115
1116     if (szUserSid && (dwContext & MSIINSTALLCONTEXT_MACHINE))
1117         return ERROR_INVALID_PARAMETER;
1118
1119     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
1120     if (rc != ERROR_SUCCESS)
1121         return rc;
1122
1123     if (dwOptions & MSISOURCETYPE_NETWORK)
1124         rc = OpenNetworkSubkey(sourcekey, &typekey, TRUE);
1125     else if (dwOptions & MSISOURCETYPE_URL)
1126         rc = OpenURLSubkey(sourcekey, &typekey, TRUE);
1127     else if (dwOptions & MSISOURCETYPE_MEDIA)
1128         rc = OpenMediaSubkey(sourcekey, &typekey, TRUE);
1129     else
1130     {
1131         ERR("unknown media type: %08x\n", dwOptions);
1132         RegCloseKey(sourcekey);
1133         return ERROR_FUNCTION_FAILED;
1134     }
1135     if (rc != ERROR_SUCCESS)
1136     {
1137         ERR("can't open subkey %u\n", rc);
1138         RegCloseKey(sourcekey);
1139         return rc;
1140     }
1141
1142     postfix = (dwOptions & MSISOURCETYPE_NETWORK) ? szBackSlash : szForwardSlash;
1143     if (szSource[lstrlenW(szSource) - 1] == *postfix)
1144         source = strdupW(szSource);
1145     else
1146     {
1147         size = lstrlenW(szSource) + 2;
1148         source = msi_alloc(size * sizeof(WCHAR));
1149         lstrcpyW(source, szSource);
1150         lstrcatW(source, postfix);
1151     }
1152
1153     list_init(&sourcelist);
1154     rc = fill_source_list(&sourcelist, typekey, &count);
1155     if (rc != ERROR_NO_MORE_ITEMS)
1156         return rc;
1157
1158     size = (lstrlenW(source) + 1) * sizeof(WCHAR);
1159
1160     if (count == 0)
1161     {
1162         rc = RegSetValueExW(typekey, szOne, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
1163         goto done;
1164     }
1165     else if (dwIndex > count || dwIndex == 0)
1166     {
1167         sprintfW(name, fmt, count + 1);
1168         rc = RegSetValueExW(typekey, name, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
1169         goto done;
1170     }
1171     else
1172     {
1173         sprintfW(name, fmt, dwIndex);
1174         info = msi_alloc(sizeof(media_info));
1175         if (!info)
1176         {
1177             rc = ERROR_OUTOFMEMORY;
1178             goto done;
1179         }
1180
1181         info->path = strdupW(source);
1182         lstrcpyW(info->szIndex, name);
1183         info->index = dwIndex;
1184         add_source_to_list(&sourcelist, info, &index);
1185
1186         LIST_FOR_EACH_ENTRY(info, &sourcelist, media_info, entry)
1187         {
1188             if (info->index < index)
1189                 continue;
1190
1191             size = (lstrlenW(info->path) + 1) * sizeof(WCHAR);
1192             rc = RegSetValueExW(typekey, info->szIndex, 0,
1193                                 REG_EXPAND_SZ, (LPBYTE)info->path, size);
1194             if (rc != ERROR_SUCCESS)
1195                 goto done;
1196         }
1197     }
1198
1199 done:
1200     free_source_list(&sourcelist);
1201     msi_free(source);
1202     RegCloseKey(typekey);
1203     RegCloseKey(sourcekey);
1204     return rc;
1205 }
1206
1207 /******************************************************************
1208  *  MsiSourceListAddMediaDiskA (MSI.@)
1209  */
1210 UINT WINAPI MsiSourceListAddMediaDiskA(LPCSTR szProduct, LPCSTR szUserSid,
1211         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId,
1212         LPCSTR szVolumeLabel, LPCSTR szDiskPrompt)
1213 {
1214     UINT r;
1215     LPWSTR product = NULL;
1216     LPWSTR usersid = NULL;
1217     LPWSTR volume = NULL;
1218     LPWSTR prompt = NULL;
1219
1220     if (szProduct) product = strdupAtoW(szProduct);
1221     if (szUserSid) usersid = strdupAtoW(szUserSid);
1222     if (szVolumeLabel) volume = strdupAtoW(szVolumeLabel);
1223     if (szDiskPrompt) prompt = strdupAtoW(szDiskPrompt);
1224
1225     r = MsiSourceListAddMediaDiskW(product, usersid, dwContext, dwOptions,
1226                                      dwDiskId, volume, prompt);
1227
1228     msi_free(product);
1229     msi_free(usersid);
1230     msi_free(volume);
1231     msi_free(prompt);
1232
1233     return r;
1234 }
1235
1236 /******************************************************************
1237  *  MsiSourceListAddMediaDiskW (MSI.@)
1238  */
1239 UINT WINAPI MsiSourceListAddMediaDiskW(LPCWSTR szProduct, LPCWSTR szUserSid, 
1240         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId, 
1241         LPCWSTR szVolumeLabel, LPCWSTR szDiskPrompt)
1242 {
1243     HKEY sourcekey;
1244     HKEY mediakey;
1245     UINT rc;
1246     WCHAR szIndex[10];
1247     WCHAR squished_pc[GUID_SIZE];
1248     LPWSTR buffer;
1249     DWORD size;
1250
1251     static const WCHAR fmt[] = {'%','i',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, szSemiColon);
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 }