msi: Add tests for MsiSourceListSetInfo.
[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)
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  *  MsiSourceListEnumSourcesA   (MSI.@)
145  */
146 UINT WINAPI MsiSourceListEnumSourcesA(LPCSTR szProductCodeOrPatch, LPCSTR szUserSid,
147                                       MSIINSTALLCONTEXT dwContext,
148                                       DWORD dwOptions, DWORD dwIndex,
149                                       LPSTR szSource, LPDWORD pcchSource)
150 {
151     FIXME("(%s, %s, %d, %d, %d, %p, %p): stub!\n", szProductCodeOrPatch, szUserSid,
152           dwContext, dwOptions, dwIndex, szSource, pcchSource);
153     return ERROR_CALL_NOT_IMPLEMENTED;
154 }
155
156 /******************************************************************
157  *  MsiSourceListGetInfoA   (MSI.@)
158  */
159 UINT WINAPI MsiSourceListGetInfoA( LPCSTR szProduct, LPCSTR szUserSid,
160                                    MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
161                                    LPCSTR szProperty, LPSTR szValue,
162                                    LPDWORD pcchValue)
163 {
164     UINT ret;
165     LPWSTR product = NULL;
166     LPWSTR usersid = NULL;
167     LPWSTR property = NULL;
168     LPWSTR value = NULL;
169     DWORD len = 0;
170
171     if (szValue && !pcchValue)
172         return ERROR_INVALID_PARAMETER;
173
174     if (szProduct) product = strdupAtoW(szProduct);
175     if (szUserSid) usersid = strdupAtoW(szUserSid);
176     if (szProperty) property = strdupAtoW(szProperty);
177
178     ret = MsiSourceListGetInfoW(product, usersid, dwContext, dwOptions,
179                                 property, NULL, &len);
180     if (ret != ERROR_SUCCESS)
181         goto done;
182
183     value = msi_alloc(++len * sizeof(WCHAR));
184     if (!value)
185         return ERROR_OUTOFMEMORY;
186
187     *value = '\0';
188     ret = MsiSourceListGetInfoW(product, usersid, dwContext, dwOptions,
189                                 property, value, &len);
190     if (ret != ERROR_SUCCESS)
191         goto done;
192
193     len = WideCharToMultiByte(CP_ACP, 0, value, -1, NULL, 0, NULL, NULL);
194     if (*pcchValue >= len)
195         WideCharToMultiByte(CP_ACP, 0, value, -1, szValue, len, NULL, NULL);
196     else if (szValue)
197         ret = ERROR_MORE_DATA;
198
199     *pcchValue = len - 1;
200
201 done:
202     msi_free(product);
203     msi_free(usersid);
204     msi_free(property);
205     msi_free(value);
206     return ret;
207 }
208
209 /******************************************************************
210  *  MsiSourceListGetInfoW   (MSI.@)
211  */
212 UINT WINAPI MsiSourceListGetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
213                                    MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
214                                    LPCWSTR szProperty, LPWSTR szValue, 
215                                    LPDWORD pcchValue) 
216 {
217     HKEY sourcekey;
218     UINT rc;
219
220     TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szProperty));
221
222     if (!szProduct || !*szProduct)
223         return ERROR_INVALID_PARAMETER;
224
225     if (lstrlenW(szProduct) != GUID_SIZE - 1 ||
226         (szProduct[0] != '{' && szProduct[GUID_SIZE - 2] != '}'))
227         return ERROR_INVALID_PARAMETER;
228
229     if (szValue && !pcchValue)
230         return ERROR_INVALID_PARAMETER;
231
232     if (dwContext != MSIINSTALLCONTEXT_USERMANAGED &&
233         dwContext != MSIINSTALLCONTEXT_USERUNMANAGED &&
234         dwContext != MSIINSTALLCONTEXT_MACHINE)
235         return ERROR_INVALID_PARAMETER;
236
237     if (!szProperty)
238         return ERROR_INVALID_PARAMETER;
239
240     if (szUserSid)
241         FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
242
243     if (dwContext != MSIINSTALLCONTEXT_USERUNMANAGED)
244         FIXME("Unhandled context %d\n", dwContext);
245
246     rc = OpenSourceKey(szProduct, &sourcekey, dwOptions, dwContext, FALSE);
247     if (rc != ERROR_SUCCESS)
248         return rc;
249
250     if (strcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) == 0)
251     {
252         HKEY key;
253         rc = OpenMediaSubkey(sourcekey, &key, FALSE);
254         if (rc == ERROR_SUCCESS)
255             rc = RegQueryValueExW(key, INSTALLPROPERTY_MEDIAPACKAGEPATHW,
256                     0, 0, (LPBYTE)szValue, pcchValue);
257         if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
258             rc = ERROR_UNKNOWN_PROPERTY;
259         RegCloseKey(key);
260     }
261     else if (strcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW) ==0)
262     {
263         HKEY key;
264         rc = OpenMediaSubkey(sourcekey, &key, FALSE);
265         if (rc == ERROR_SUCCESS)
266             rc = RegQueryValueExW(key, INSTALLPROPERTY_DISKPROMPTW, 0, 0,
267                     (LPBYTE)szValue, pcchValue);
268         if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
269             rc = ERROR_UNKNOWN_PROPERTY;
270         RegCloseKey(key);
271     }
272     else if (strcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW)==0)
273     {
274         LPWSTR buffer;
275         DWORD size = 0;
276
277         RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0,
278                 NULL, &size);
279         if (size == 0)
280             rc = ERROR_UNKNOWN_PROPERTY;
281         else
282         {
283             LPWSTR ptr;
284             buffer = msi_alloc(size);
285             rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
286                     0, 0, (LPBYTE)buffer,&size); 
287             ptr = strchrW(buffer,';');
288             if (ptr) ptr = strchrW(ptr+1,';');
289             if (!ptr)
290                 rc = ERROR_UNKNOWN_PROPERTY;
291             else
292             {
293                 ptr ++;
294                 lstrcpynW(szValue, ptr, *pcchValue);
295                 if (lstrlenW(ptr) > *pcchValue)
296                 {
297                     *pcchValue = lstrlenW(ptr)+1;
298                     rc = ERROR_MORE_DATA;
299                 }
300                 else
301                     rc = ERROR_SUCCESS;
302             }
303             msi_free(buffer);
304         }
305     }
306     else if (strcmpW(INSTALLPROPERTY_LASTUSEDTYPEW, szProperty)==0)
307     {
308         LPWSTR buffer;
309         DWORD size = 0;
310
311         RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0,
312                 NULL, &size);
313         if (size == 0)
314             rc = ERROR_UNKNOWN_PROPERTY;
315         else
316         {
317             buffer = msi_alloc(size);
318             rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
319                     0, 0, (LPBYTE)buffer,&size); 
320             if (*pcchValue < 1)
321             {
322                 rc = ERROR_MORE_DATA;
323                 *pcchValue = 1;
324             }
325             else
326             {
327                 szValue[0] = buffer[0];
328                 rc = ERROR_SUCCESS;
329             }
330             msi_free(buffer);
331         }
332     }
333     else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0)
334     {
335         *pcchValue = *pcchValue * sizeof(WCHAR);
336         rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, 0,
337                               (LPBYTE)szValue, pcchValue);
338         if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
339         {
340             *pcchValue = 0;
341             rc = ERROR_SUCCESS;
342         }
343         else
344         {
345             if (*pcchValue)
346                 *pcchValue = (*pcchValue - 1) / sizeof(WCHAR);
347             if (szValue)
348                 szValue[*pcchValue] = '\0';
349         }
350     }
351     else
352     {
353         FIXME("Unknown property %s\n",debugstr_w(szProperty));
354         rc = ERROR_UNKNOWN_PROPERTY;
355     }
356
357     RegCloseKey(sourcekey);
358     return rc;
359 }
360
361 /******************************************************************
362  *  MsiSourceListSetInfoA   (MSI.@)
363  */
364 UINT WINAPI MsiSourceListSetInfoA(LPCSTR szProduct, LPCSTR szUserSid,
365                                   MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
366                                   LPCSTR szProperty, LPCSTR szValue)
367 {
368     UINT ret;
369     LPWSTR product = NULL;
370     LPWSTR usersid = NULL;
371     LPWSTR property = NULL;
372     LPWSTR value = NULL;
373
374     if (szProduct) product = strdupAtoW(szProduct);
375     if (szUserSid) usersid = strdupAtoW(szUserSid);
376     if (szProperty) property = strdupAtoW(szProperty);
377     if (szValue) value = strdupAtoW(szValue);
378
379     ret = MsiSourceListSetInfoW(product, usersid, dwContext, dwOptions,
380                                 property, value);
381
382     msi_free(product);
383     msi_free(usersid);
384     msi_free(property);
385     msi_free(value);
386
387     return ret;
388 }
389
390 /******************************************************************
391  *  MsiSourceListSetInfoW   (MSI.@)
392  */
393 UINT WINAPI MsiSourceListSetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
394                                    MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
395                                    LPCWSTR szProperty, LPCWSTR szValue)
396 {
397     HKEY sourcekey;
398     UINT rc;
399
400     TRACE("%s %s %x %x %s %s\n", debugstr_w(szProduct), debugstr_w(szUserSid),
401             dwContext, dwOptions, debugstr_w(szProperty), debugstr_w(szValue));
402
403     if (!szProduct || lstrlenW(szProduct) > 39)
404         return ERROR_INVALID_PARAMETER;
405
406     if (!szProperty)
407         return ERROR_INVALID_PARAMETER;
408
409     if (!szValue)
410         return ERROR_UNKNOWN_PROPERTY;
411
412     if (dwOptions & MSICODE_PATCH)
413     {
414         FIXME("Unhandled options MSICODE_PATCH\n");
415         return ERROR_FUNCTION_FAILED;
416     }
417     
418     if (szUserSid)
419         FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
420
421     if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
422         FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
423
424     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
425     if (rc != ERROR_SUCCESS)
426         return ERROR_UNKNOWN_PRODUCT;
427
428
429     if (strcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) == 0)
430     {
431         HKEY key;
432         DWORD size = lstrlenW(szValue)*sizeof(WCHAR);
433         rc = OpenMediaSubkey(sourcekey, &key, FALSE);
434         if (rc == ERROR_SUCCESS)
435             rc = RegSetValueExW(key, INSTALLPROPERTY_MEDIAPACKAGEPATHW, 0,
436                     REG_SZ, (const BYTE *)szValue, size);
437         if (rc != ERROR_SUCCESS)
438             rc = ERROR_UNKNOWN_PROPERTY;
439         RegCloseKey(key);
440     }
441     else if (strcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW) == 0)
442     {
443         HKEY key;
444         DWORD size = lstrlenW(szValue)*sizeof(WCHAR);
445         rc = OpenMediaSubkey(sourcekey, &key, FALSE);
446         if (rc == ERROR_SUCCESS)
447             rc = RegSetValueExW(key, INSTALLPROPERTY_DISKPROMPTW, 0,
448                     REG_SZ, (const BYTE *)szValue, size);
449         if (rc != ERROR_SUCCESS)
450             rc = ERROR_UNKNOWN_PROPERTY;
451         RegCloseKey(key);
452     }
453     else if (strcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW)==0)
454     {
455         LPWSTR buffer = NULL;
456         DWORD size;
457         WCHAR typechar = 'n';
458         static const WCHAR LastUsedSource_Fmt[] = {'%','c',';','%','i',';','%','s',0};
459
460         /* make sure the source is registered */
461         MsiSourceListAddSourceExW(szProduct, szUserSid, dwContext, 
462                 dwOptions, szValue, 0); 
463
464         if (dwOptions & MSISOURCETYPE_NETWORK)
465             typechar = 'n';
466         else if (dwOptions & MSISOURCETYPE_URL)
467             typechar = 'u';
468         else if (dwOptions & MSISOURCETYPE_MEDIA)
469             typechar = 'm';
470         else
471             ERR("Unknown source type! %x\n", dwOptions);
472
473         size = (lstrlenW(szValue)+5)*sizeof(WCHAR);
474         buffer = msi_alloc(size);
475         sprintfW(buffer, LastUsedSource_Fmt, typechar, 1, szValue);
476         rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 
477                 REG_EXPAND_SZ, (LPBYTE)buffer, size);
478         if (rc != ERROR_SUCCESS)
479             rc = ERROR_UNKNOWN_PROPERTY;
480         msi_free( buffer );
481     }
482     else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0)
483     {
484         DWORD size = lstrlenW(szValue)*sizeof(WCHAR);
485         rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0,
486                 REG_SZ, (const BYTE *)szValue, size);
487         if (rc != ERROR_SUCCESS)
488             rc = ERROR_UNKNOWN_PROPERTY;
489     }
490     else
491     {
492         FIXME("Unknown property %s\n",debugstr_w(szProperty));
493         rc = ERROR_UNKNOWN_PROPERTY;
494     }
495
496     RegCloseKey(sourcekey);
497     return rc;
498
499 }
500
501 /******************************************************************
502  *  MsiSourceListAddSourceW (MSI.@)
503  */
504 UINT WINAPI MsiSourceListAddSourceW( LPCWSTR szProduct, LPCWSTR szUserName,
505         DWORD dwReserved, LPCWSTR szSource)
506 {
507     INT ret;
508     LPWSTR sidstr = NULL;
509     DWORD sidsize = 0;
510     DWORD domsize = 0;
511
512     TRACE("%s %s %s\n", debugstr_w(szProduct), debugstr_w(szUserName), debugstr_w(szSource));
513
514     if (LookupAccountNameW(NULL, szUserName, NULL, &sidsize, NULL, &domsize, NULL))
515     {
516         PSID psid = msi_alloc(sidsize);
517
518         if (LookupAccountNameW(NULL, szUserName, psid, &sidsize, NULL, &domsize, NULL))
519             ConvertSidToStringSidW(psid, &sidstr);
520
521         msi_free(psid);
522     }
523
524     ret = MsiSourceListAddSourceExW(szProduct, sidstr, 
525         MSIINSTALLCONTEXT_USERMANAGED, MSISOURCETYPE_NETWORK, szSource, 0);
526
527     if (sidstr)
528         LocalFree(sidstr);
529
530     return ret;
531 }
532
533 /******************************************************************
534  *  MsiSourceListAddSourceA (MSI.@)
535  */
536 UINT WINAPI MsiSourceListAddSourceA( LPCSTR szProduct, LPCSTR szUserName,
537         DWORD dwReserved, LPCSTR szSource)
538 {
539     INT ret;
540     LPWSTR szwproduct;
541     LPWSTR szwusername;
542     LPWSTR szwsource;
543
544     szwproduct = strdupAtoW( szProduct );
545     szwusername = strdupAtoW( szUserName );
546     szwsource = strdupAtoW( szSource );
547
548     ret = MsiSourceListAddSourceW(szwproduct, szwusername, 0, szwsource);
549
550     msi_free(szwproduct);
551     msi_free(szwusername);
552     msi_free(szwsource);
553
554     return ret;
555 }
556
557 /******************************************************************
558  *  MsiSourceListAddSourceExA (MSI.@)
559  */
560 UINT WINAPI MsiSourceListAddSourceExA(LPCSTR szProduct, LPCSTR szUserSid,
561         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCSTR szSource, DWORD dwIndex)
562 {
563     UINT ret;
564     LPWSTR product, usersid, source;
565
566     product = strdupAtoW(szProduct);
567     usersid = strdupAtoW(szUserSid);
568     source = strdupAtoW(szSource);
569
570     ret = MsiSourceListAddSourceExW(product, usersid, dwContext,
571                                     dwOptions, source, dwIndex);
572
573     msi_free(product);
574     msi_free(usersid);
575     msi_free(source);
576
577     return ret;
578 }
579
580 static void free_source_list(struct list *sourcelist)
581 {
582     while (!list_empty(sourcelist))
583     {
584         media_info *info = LIST_ENTRY(list_head(sourcelist), media_info, entry);
585         list_remove(&info->entry);
586         msi_free(info->path);
587         msi_free(info);
588     }
589 }
590
591 static void add_source_to_list(struct list *sourcelist, media_info *info)
592 {
593     media_info *iter;
594     BOOL found = FALSE;
595     static const WCHAR fmt[] = {'%','i',0};
596
597     if (list_empty(sourcelist))
598     {
599         list_add_head(sourcelist, &info->entry);
600         return;
601     }
602
603     LIST_FOR_EACH_ENTRY(iter, sourcelist, media_info, entry)
604     {
605         if (!found && info->index < iter->index)
606         {
607             found = TRUE;
608             list_add_before(&iter->entry, &info->entry);
609         }
610
611         /* update the rest of the list */
612         if (found)
613             sprintfW(iter->szIndex, fmt, ++iter->index);
614     }
615
616     if (!found)
617         list_add_after(&iter->entry, &info->entry);
618 }
619
620 static UINT fill_source_list(struct list *sourcelist, HKEY sourcekey, DWORD *count)
621 {
622     UINT r = ERROR_SUCCESS;
623     DWORD index = 0;
624     WCHAR name[10];
625     DWORD size, val_size;
626     media_info *entry;
627
628     *count = 0;
629
630     while (r == ERROR_SUCCESS)
631     {
632         size = sizeof(name) / sizeof(name[0]);
633         r = RegEnumValueW(sourcekey, index, name, &size, NULL, NULL, NULL, &val_size);
634         if (r != ERROR_SUCCESS)
635             return r;
636
637         entry = msi_alloc(sizeof(media_info));
638         if (!entry)
639             goto error;
640
641         entry->path = msi_alloc(val_size);
642         if (!entry->path)
643         {
644             msi_free(entry);
645             goto error;
646         }
647
648         lstrcpyW(entry->szIndex, name);
649         entry->index = atoiW(name);
650
651         size++;
652         r = RegEnumValueW(sourcekey, index, name, &size, NULL,
653                           NULL, (LPBYTE)entry->path, &val_size);
654         if (r != ERROR_SUCCESS)
655         {
656             msi_free(entry->path);
657             msi_free(entry);
658             goto error;
659         }
660
661         index = ++(*count);
662         add_source_to_list(sourcelist, entry);
663     }
664
665 error:
666     *count = -1;
667     free_source_list(sourcelist);
668     return ERROR_OUTOFMEMORY;
669 }
670
671 /******************************************************************
672  *  MsiSourceListAddSourceExW (MSI.@)
673  */
674 UINT WINAPI MsiSourceListAddSourceExW( LPCWSTR szProduct, LPCWSTR szUserSid,
675         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szSource, 
676         DWORD dwIndex)
677 {
678     HKEY sourcekey;
679     HKEY typekey;
680     UINT rc;
681     struct list sourcelist;
682     media_info *info;
683     WCHAR squished_pc[GUID_SIZE];
684     WCHAR name[10];
685     LPWSTR source;
686     LPCWSTR postfix;
687     DWORD size, count;
688
689     static const WCHAR fmt[] = {'%','i',0};
690     static const WCHAR one[] = {'1',0};
691     static const WCHAR backslash[] = {'\\',0};
692     static const WCHAR forwardslash[] = {'/',0};
693
694     TRACE("%s %s %x %x %s %i\n", debugstr_w(szProduct), debugstr_w(szUserSid),
695           dwContext, dwOptions, debugstr_w(szSource), dwIndex);
696
697     if (!szProduct || !squash_guid(szProduct, squished_pc))
698         return ERROR_INVALID_PARAMETER;
699
700     if (!szSource || !*szSource)
701         return ERROR_INVALID_PARAMETER;
702
703     if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
704         return ERROR_INVALID_PARAMETER;
705
706     if (dwOptions & MSICODE_PATCH)
707     {
708         FIXME("Unhandled options MSICODE_PATCH\n");
709         return ERROR_FUNCTION_FAILED;
710     }
711
712     if (szUserSid && (dwContext & MSIINSTALLCONTEXT_MACHINE))
713         return ERROR_INVALID_PARAMETER;
714
715     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
716     if (rc != ERROR_SUCCESS)
717         return rc;
718
719     if (dwOptions & MSISOURCETYPE_NETWORK)
720         rc = OpenNetworkSubkey(sourcekey, &typekey, TRUE);
721     else if (dwOptions & MSISOURCETYPE_URL)
722         rc = OpenURLSubkey(sourcekey, &typekey, TRUE);
723     else if (dwOptions & MSISOURCETYPE_MEDIA)
724         rc = OpenMediaSubkey(sourcekey, &typekey, TRUE);
725     else
726     {
727         ERR("unknown media type: %08x\n", dwOptions);
728         RegCloseKey(sourcekey);
729         return ERROR_FUNCTION_FAILED;
730     }
731
732     postfix = (dwOptions & MSISOURCETYPE_NETWORK) ? backslash : forwardslash;
733     if (szSource[lstrlenW(szSource) - 1] == *postfix)
734         source = strdupW(szSource);
735     else
736     {
737         size = lstrlenW(szSource) + 2;
738         source = msi_alloc(size * sizeof(WCHAR));
739         lstrcpyW(source, szSource);
740         lstrcatW(source, postfix);
741     }
742
743     list_init(&sourcelist);
744     rc = fill_source_list(&sourcelist, typekey, &count);
745     if (rc != ERROR_NO_MORE_ITEMS)
746         return rc;
747
748     size = (lstrlenW(source) + 1) * sizeof(WCHAR);
749
750     if (count == 0)
751     {
752         rc = RegSetValueExW(typekey, one, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
753         goto done;
754     }
755     else if (dwIndex > count)
756     {
757         sprintfW(name, fmt, count + 1);
758         rc = RegSetValueExW(typekey, name, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
759         goto done;
760     }
761     else
762     {
763         /* add to the end of the list */
764         if (dwIndex == 0)
765             dwIndex = count + 1;
766
767         sprintfW(name, fmt, dwIndex);
768         info = msi_alloc(sizeof(media_info));
769         if (!info)
770         {
771             rc = ERROR_OUTOFMEMORY;
772             goto done;
773         }
774
775         info->path = strdupW(source);
776         lstrcpyW(info->szIndex, name);
777         info->index = dwIndex;
778         add_source_to_list(&sourcelist, info);
779
780         LIST_FOR_EACH_ENTRY(info, &sourcelist, media_info, entry)
781         {
782             size = (lstrlenW(info->path) + 1) * sizeof(WCHAR);
783             rc = RegSetValueExW(typekey, info->szIndex, 0,
784                                 REG_EXPAND_SZ, (LPBYTE)info->path, size);
785             if (rc != ERROR_SUCCESS)
786                 goto done;
787         }
788     }
789
790 done:
791     free_source_list(&sourcelist);
792     msi_free(source);
793     RegCloseKey(typekey);
794     RegCloseKey(sourcekey);
795     return rc;
796 }
797
798 /******************************************************************
799  *  MsiSourceListAddMediaDisk(MSI.@)
800  */
801 UINT WINAPI MsiSourceListAddMediaDiskW(LPCWSTR szProduct, LPCWSTR szUserSid, 
802         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId, 
803         LPCWSTR szVolumeLabel, LPCWSTR szDiskPrompt)
804 {
805     HKEY sourcekey;
806     HKEY mediakey;
807     UINT rc;
808     WCHAR szIndex[10];
809     static const WCHAR fmt[] = {'%','i',0};
810     static const WCHAR disk_fmt[] = {'%','s',';','%','s',0};
811     static const WCHAR empty[1] = {0};
812     LPCWSTR pt1,pt2;
813     LPWSTR buffer;
814     DWORD size;
815
816     TRACE("%s %s %x %x %i %s %s\n", debugstr_w(szProduct),
817             debugstr_w(szUserSid), dwContext, dwOptions, dwDiskId,
818             debugstr_w(szVolumeLabel), debugstr_w(szDiskPrompt));
819
820     if (!szProduct || lstrlenW(szProduct) > 39)
821         return ERROR_INVALID_PARAMETER;
822
823     if (dwOptions & MSICODE_PATCH)
824     {
825         FIXME("Unhandled options MSICODE_PATCH\n");
826         return ERROR_FUNCTION_FAILED;
827     }
828     
829     if (szUserSid)
830         FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
831
832     if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
833         FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
834
835     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, TRUE);
836     if (rc != ERROR_SUCCESS)
837         return ERROR_UNKNOWN_PRODUCT;
838
839     OpenMediaSubkey(sourcekey,&mediakey,TRUE);
840
841     sprintfW(szIndex,fmt,dwDiskId);
842
843     size = 2;
844     if (szVolumeLabel)
845     {
846         size +=lstrlenW(szVolumeLabel);
847         pt1 = szVolumeLabel;
848     }
849     else
850         pt1 = empty;
851     if (szDiskPrompt)
852     {
853         size +=lstrlenW(szDiskPrompt);
854         pt2 = szDiskPrompt;
855     }
856     else
857         pt2 = empty;
858
859     size *=sizeof(WCHAR);
860
861     buffer = msi_alloc(size);
862     sprintfW(buffer,disk_fmt,pt1,pt2);
863
864     RegSetValueExW(mediakey, szIndex, 0, REG_SZ, (LPBYTE)buffer, size);
865     msi_free( buffer );
866
867     RegCloseKey(sourcekey);
868     RegCloseKey(mediakey);
869
870     return ERROR_SUCCESS;
871 }
872
873 /******************************************************************
874  *  MsiSourceListClearAllA (MSI.@)
875  */
876 UINT WINAPI MsiSourceListClearAllA( LPCSTR szProduct, LPCSTR szUserName, DWORD dwReserved )
877 {
878     FIXME("(%s %s %d)\n", debugstr_a(szProduct), debugstr_a(szUserName), dwReserved);
879     return ERROR_SUCCESS;
880 }
881
882 /******************************************************************
883  *  MsiSourceListClearAllW (MSI.@)
884  */
885 UINT WINAPI MsiSourceListClearAllW( LPCWSTR szProduct, LPCWSTR szUserName, DWORD dwReserved )
886 {
887     FIXME("(%s %s %d)\n", debugstr_w(szProduct), debugstr_w(szUserName), dwReserved);
888     return ERROR_SUCCESS;
889 }