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