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