user32: Assign to structs instead of using memcpy.
[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     WCHAR squished_pc[GUID_SIZE];
854     INT ret;
855     LPWSTR sidstr = NULL;
856     DWORD sidsize = 0;
857     DWORD domsize = 0;
858     DWORD context;
859     HKEY hkey = 0;
860     UINT r;
861
862     TRACE("%s %s %s\n", debugstr_w(szProduct), debugstr_w(szUserName), debugstr_w(szSource));
863
864     if (!szSource || !*szSource)
865         return ERROR_INVALID_PARAMETER;
866
867     if (dwReserved != 0)
868         return ERROR_INVALID_PARAMETER;
869
870     if (!szProduct || !squash_guid(szProduct, squished_pc))
871         return ERROR_INVALID_PARAMETER;
872
873     if (!szUserName || !*szUserName)
874         context = MSIINSTALLCONTEXT_MACHINE;
875     else
876     {
877         if (LookupAccountNameW(NULL, szUserName, NULL, &sidsize, NULL, &domsize, NULL))
878         {
879             PSID psid = msi_alloc(sidsize);
880
881             if (LookupAccountNameW(NULL, szUserName, psid, &sidsize, NULL, &domsize, NULL))
882                 ConvertSidToStringSidW(psid, &sidstr);
883
884             msi_free(psid);
885         }
886
887         r = MSIREG_OpenLocalManagedProductKey(szProduct, &hkey, FALSE);
888         if (r == ERROR_SUCCESS)
889             context = MSIINSTALLCONTEXT_USERMANAGED;
890         else
891         {
892             r = MSIREG_OpenUserProductsKey(szProduct, &hkey, FALSE);
893             if (r != ERROR_SUCCESS)
894                 return ERROR_UNKNOWN_PRODUCT;
895
896             context = MSIINSTALLCONTEXT_USERUNMANAGED;
897         }
898
899         RegCloseKey(hkey);
900     }
901
902     ret = MsiSourceListAddSourceExW(szProduct, sidstr, 
903         context, MSISOURCETYPE_NETWORK, szSource, 0);
904
905     if (sidstr)
906         LocalFree(sidstr);
907
908     return ret;
909 }
910
911 /******************************************************************
912  *  MsiSourceListAddSourceA (MSI.@)
913  */
914 UINT WINAPI MsiSourceListAddSourceA( LPCSTR szProduct, LPCSTR szUserName,
915         DWORD dwReserved, LPCSTR szSource)
916 {
917     INT ret;
918     LPWSTR szwproduct;
919     LPWSTR szwusername;
920     LPWSTR szwsource;
921
922     szwproduct = strdupAtoW( szProduct );
923     szwusername = strdupAtoW( szUserName );
924     szwsource = strdupAtoW( szSource );
925
926     ret = MsiSourceListAddSourceW(szwproduct, szwusername, dwReserved, szwsource);
927
928     msi_free(szwproduct);
929     msi_free(szwusername);
930     msi_free(szwsource);
931
932     return ret;
933 }
934
935 /******************************************************************
936  *  MsiSourceListAddSourceExA (MSI.@)
937  */
938 UINT WINAPI MsiSourceListAddSourceExA(LPCSTR szProduct, LPCSTR szUserSid,
939         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCSTR szSource, DWORD dwIndex)
940 {
941     UINT ret;
942     LPWSTR product, usersid, source;
943
944     product = strdupAtoW(szProduct);
945     usersid = strdupAtoW(szUserSid);
946     source = strdupAtoW(szSource);
947
948     ret = MsiSourceListAddSourceExW(product, usersid, dwContext,
949                                     dwOptions, source, dwIndex);
950
951     msi_free(product);
952     msi_free(usersid);
953     msi_free(source);
954
955     return ret;
956 }
957
958 static void free_source_list(struct list *sourcelist)
959 {
960     while (!list_empty(sourcelist))
961     {
962         media_info *info = LIST_ENTRY(list_head(sourcelist), media_info, entry);
963         list_remove(&info->entry);
964         msi_free(info->path);
965         msi_free(info);
966     }
967 }
968
969 static void add_source_to_list(struct list *sourcelist, media_info *info,
970                                DWORD *index)
971 {
972     media_info *iter;
973     BOOL found = FALSE;
974     static const WCHAR fmt[] = {'%','i',0};
975
976     if (index) *index = 0;
977
978     if (list_empty(sourcelist))
979     {
980         list_add_head(sourcelist, &info->entry);
981         return;
982     }
983
984     LIST_FOR_EACH_ENTRY(iter, sourcelist, media_info, entry)
985     {
986         if (!found && info->index < iter->index)
987         {
988             found = TRUE;
989             list_add_before(&iter->entry, &info->entry);
990         }
991
992         /* update the rest of the list */
993         if (found)
994             sprintfW(iter->szIndex, fmt, ++iter->index);
995         else if (index)
996             (*index)++;
997     }
998
999     if (!found)
1000         list_add_after(&iter->entry, &info->entry);
1001 }
1002
1003 static UINT fill_source_list(struct list *sourcelist, HKEY sourcekey, DWORD *count)
1004 {
1005     UINT r = ERROR_SUCCESS;
1006     DWORD index = 0;
1007     WCHAR name[10];
1008     DWORD size, val_size;
1009     media_info *entry;
1010
1011     *count = 0;
1012
1013     while (r == ERROR_SUCCESS)
1014     {
1015         size = sizeof(name) / sizeof(name[0]);
1016         r = RegEnumValueW(sourcekey, index, name, &size, NULL, NULL, NULL, &val_size);
1017         if (r != ERROR_SUCCESS)
1018             return r;
1019
1020         entry = msi_alloc(sizeof(media_info));
1021         if (!entry)
1022             goto error;
1023
1024         entry->path = msi_alloc(val_size);
1025         if (!entry->path)
1026         {
1027             msi_free(entry);
1028             goto error;
1029         }
1030
1031         lstrcpyW(entry->szIndex, name);
1032         entry->index = atoiW(name);
1033
1034         size++;
1035         r = RegEnumValueW(sourcekey, index, name, &size, NULL,
1036                           NULL, (LPBYTE)entry->path, &val_size);
1037         if (r != ERROR_SUCCESS)
1038         {
1039             msi_free(entry->path);
1040             msi_free(entry);
1041             goto error;
1042         }
1043
1044         index = ++(*count);
1045         add_source_to_list(sourcelist, entry, NULL);
1046     }
1047
1048 error:
1049     *count = -1;
1050     free_source_list(sourcelist);
1051     return ERROR_OUTOFMEMORY;
1052 }
1053
1054 /******************************************************************
1055  *  MsiSourceListAddSourceExW (MSI.@)
1056  */
1057 UINT WINAPI MsiSourceListAddSourceExW( LPCWSTR szProduct, LPCWSTR szUserSid,
1058         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szSource, 
1059         DWORD dwIndex)
1060 {
1061     HKEY sourcekey;
1062     HKEY typekey;
1063     UINT rc;
1064     struct list sourcelist;
1065     media_info *info;
1066     WCHAR squished_pc[GUID_SIZE];
1067     WCHAR name[10];
1068     LPWSTR source;
1069     LPCWSTR postfix;
1070     DWORD size, count;
1071     DWORD index;
1072
1073     static const WCHAR fmt[] = {'%','i',0};
1074     static const WCHAR one[] = {'1',0};
1075     static const WCHAR backslash[] = {'\\',0};
1076     static const WCHAR forwardslash[] = {'/',0};
1077
1078     TRACE("%s %s %x %x %s %i\n", debugstr_w(szProduct), debugstr_w(szUserSid),
1079           dwContext, dwOptions, debugstr_w(szSource), dwIndex);
1080
1081     if (!szProduct || !squash_guid(szProduct, squished_pc))
1082         return ERROR_INVALID_PARAMETER;
1083
1084     if (!szSource || !*szSource)
1085         return ERROR_INVALID_PARAMETER;
1086
1087     if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
1088         return ERROR_INVALID_PARAMETER;
1089
1090     if (dwOptions & MSICODE_PATCH)
1091     {
1092         FIXME("Unhandled options MSICODE_PATCH\n");
1093         return ERROR_FUNCTION_FAILED;
1094     }
1095
1096     if (szUserSid && (dwContext & MSIINSTALLCONTEXT_MACHINE))
1097         return ERROR_INVALID_PARAMETER;
1098
1099     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
1100     if (rc != ERROR_SUCCESS)
1101         return rc;
1102
1103     if (dwOptions & MSISOURCETYPE_NETWORK)
1104         rc = OpenNetworkSubkey(sourcekey, &typekey, TRUE);
1105     else if (dwOptions & MSISOURCETYPE_URL)
1106         rc = OpenURLSubkey(sourcekey, &typekey, TRUE);
1107     else if (dwOptions & MSISOURCETYPE_MEDIA)
1108         rc = OpenMediaSubkey(sourcekey, &typekey, TRUE);
1109     else
1110     {
1111         ERR("unknown media type: %08x\n", dwOptions);
1112         RegCloseKey(sourcekey);
1113         return ERROR_FUNCTION_FAILED;
1114     }
1115
1116     postfix = (dwOptions & MSISOURCETYPE_NETWORK) ? backslash : forwardslash;
1117     if (szSource[lstrlenW(szSource) - 1] == *postfix)
1118         source = strdupW(szSource);
1119     else
1120     {
1121         size = lstrlenW(szSource) + 2;
1122         source = msi_alloc(size * sizeof(WCHAR));
1123         lstrcpyW(source, szSource);
1124         lstrcatW(source, postfix);
1125     }
1126
1127     list_init(&sourcelist);
1128     rc = fill_source_list(&sourcelist, typekey, &count);
1129     if (rc != ERROR_NO_MORE_ITEMS)
1130         return rc;
1131
1132     size = (lstrlenW(source) + 1) * sizeof(WCHAR);
1133
1134     if (count == 0)
1135     {
1136         rc = RegSetValueExW(typekey, one, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
1137         goto done;
1138     }
1139     else if (dwIndex > count || dwIndex == 0)
1140     {
1141         sprintfW(name, fmt, count + 1);
1142         rc = RegSetValueExW(typekey, name, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
1143         goto done;
1144     }
1145     else
1146     {
1147         sprintfW(name, fmt, dwIndex);
1148         info = msi_alloc(sizeof(media_info));
1149         if (!info)
1150         {
1151             rc = ERROR_OUTOFMEMORY;
1152             goto done;
1153         }
1154
1155         info->path = strdupW(source);
1156         lstrcpyW(info->szIndex, name);
1157         info->index = dwIndex;
1158         add_source_to_list(&sourcelist, info, &index);
1159
1160         LIST_FOR_EACH_ENTRY(info, &sourcelist, media_info, entry)
1161         {
1162             if (info->index < index)
1163                 continue;
1164
1165             size = (lstrlenW(info->path) + 1) * sizeof(WCHAR);
1166             rc = RegSetValueExW(typekey, info->szIndex, 0,
1167                                 REG_EXPAND_SZ, (LPBYTE)info->path, size);
1168             if (rc != ERROR_SUCCESS)
1169                 goto done;
1170         }
1171     }
1172
1173 done:
1174     free_source_list(&sourcelist);
1175     msi_free(source);
1176     RegCloseKey(typekey);
1177     RegCloseKey(sourcekey);
1178     return rc;
1179 }
1180
1181 /******************************************************************
1182  *  MsiSourceListAddMediaDiskA (MSI.@)
1183  */
1184 UINT WINAPI MsiSourceListAddMediaDiskA(LPCSTR szProduct, LPCSTR szUserSid,
1185         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId,
1186         LPCSTR szVolumeLabel, LPCSTR szDiskPrompt)
1187 {
1188     UINT r;
1189     LPWSTR product = NULL;
1190     LPWSTR usersid = NULL;
1191     LPWSTR volume = NULL;
1192     LPWSTR prompt = NULL;
1193
1194     if (szProduct) product = strdupAtoW(szProduct);
1195     if (szUserSid) usersid = strdupAtoW(szUserSid);
1196     if (szVolumeLabel) volume = strdupAtoW(szVolumeLabel);
1197     if (szDiskPrompt) prompt = strdupAtoW(szDiskPrompt);
1198
1199     r = MsiSourceListAddMediaDiskW(product, usersid, dwContext, dwOptions,
1200                                      dwDiskId, volume, prompt);
1201
1202     msi_free(product);
1203     msi_free(usersid);
1204     msi_free(volume);
1205     msi_free(prompt);
1206
1207     return r;
1208 }
1209
1210 /******************************************************************
1211  *  MsiSourceListAddMediaDiskW (MSI.@)
1212  */
1213 UINT WINAPI MsiSourceListAddMediaDiskW(LPCWSTR szProduct, LPCWSTR szUserSid, 
1214         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId, 
1215         LPCWSTR szVolumeLabel, LPCWSTR szDiskPrompt)
1216 {
1217     HKEY sourcekey;
1218     HKEY mediakey;
1219     UINT rc;
1220     WCHAR szIndex[10];
1221     WCHAR squished_pc[GUID_SIZE];
1222     LPWSTR buffer;
1223     DWORD size;
1224
1225     static const WCHAR fmt[] = {'%','i',0};
1226     static const WCHAR semicolon[] = {';',0};
1227
1228     TRACE("%s %s %x %x %i %s %s\n", debugstr_w(szProduct),
1229             debugstr_w(szUserSid), dwContext, dwOptions, dwDiskId,
1230             debugstr_w(szVolumeLabel), debugstr_w(szDiskPrompt));
1231
1232     if (!szProduct || !squash_guid(szProduct, squished_pc))
1233         return ERROR_INVALID_PARAMETER;
1234
1235     if (dwOptions != MSICODE_PRODUCT && dwOptions != MSICODE_PATCH)
1236         return ERROR_INVALID_PARAMETER;
1237
1238     if ((szVolumeLabel && !*szVolumeLabel) || (szDiskPrompt && !*szDiskPrompt))
1239         return ERROR_INVALID_PARAMETER;
1240
1241     if ((dwContext & MSIINSTALLCONTEXT_MACHINE) && szUserSid)
1242         return ERROR_INVALID_PARAMETER;
1243
1244     if (dwOptions & MSICODE_PATCH)
1245     {
1246         FIXME("Unhandled options MSICODE_PATCH\n");
1247         return ERROR_FUNCTION_FAILED;
1248     }
1249
1250     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
1251     if (rc != ERROR_SUCCESS)
1252         return rc;
1253
1254     OpenMediaSubkey(sourcekey, &mediakey, TRUE);
1255
1256     sprintfW(szIndex, fmt, dwDiskId);
1257
1258     size = 2;
1259     if (szVolumeLabel) size += lstrlenW(szVolumeLabel);
1260     if (szDiskPrompt) size += lstrlenW(szDiskPrompt);
1261
1262     size *= sizeof(WCHAR);
1263     buffer = msi_alloc(size);
1264     *buffer = '\0';
1265
1266     if (szVolumeLabel) lstrcpyW(buffer, szVolumeLabel);
1267     lstrcatW(buffer, semicolon);
1268     if (szDiskPrompt) lstrcatW(buffer, szDiskPrompt);
1269
1270     RegSetValueExW(mediakey, szIndex, 0, REG_SZ, (LPBYTE)buffer, size);
1271     msi_free(buffer);
1272
1273     RegCloseKey(sourcekey);
1274     RegCloseKey(mediakey);
1275
1276     return ERROR_SUCCESS;
1277 }
1278
1279 /******************************************************************
1280  *  MsiSourceListClearAllA (MSI.@)
1281  */
1282 UINT WINAPI MsiSourceListClearAllA( LPCSTR szProduct, LPCSTR szUserName, DWORD dwReserved )
1283 {
1284     FIXME("(%s %s %d)\n", debugstr_a(szProduct), debugstr_a(szUserName), dwReserved);
1285     return ERROR_SUCCESS;
1286 }
1287
1288 /******************************************************************
1289  *  MsiSourceListClearAllW (MSI.@)
1290  */
1291 UINT WINAPI MsiSourceListClearAllW( LPCWSTR szProduct, LPCWSTR szUserName, DWORD dwReserved )
1292 {
1293     FIXME("(%s %s %d)\n", debugstr_w(szProduct), debugstr_w(szUserName), dwReserved);
1294     return ERROR_SUCCESS;
1295 }