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