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