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