msi: Fix the size of the data sent in to RegSetValueExW.
[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     HKEY sourcekey;
355     UINT rc;
356
357     TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szProperty));
358
359     if (!szProduct || !*szProduct)
360         return ERROR_INVALID_PARAMETER;
361
362     if (lstrlenW(szProduct) != GUID_SIZE - 1 ||
363         (szProduct[0] != '{' && szProduct[GUID_SIZE - 2] != '}'))
364         return ERROR_INVALID_PARAMETER;
365
366     if (szValue && !pcchValue)
367         return ERROR_INVALID_PARAMETER;
368
369     if (dwContext != MSIINSTALLCONTEXT_USERMANAGED &&
370         dwContext != MSIINSTALLCONTEXT_USERUNMANAGED &&
371         dwContext != MSIINSTALLCONTEXT_MACHINE)
372         return ERROR_INVALID_PARAMETER;
373
374     if (!szProperty)
375         return ERROR_INVALID_PARAMETER;
376
377     if (szUserSid)
378         FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
379
380     if (dwContext != MSIINSTALLCONTEXT_USERUNMANAGED)
381         FIXME("Unhandled context %d\n", dwContext);
382
383     rc = OpenSourceKey(szProduct, &sourcekey, dwOptions, dwContext, FALSE);
384     if (rc != ERROR_SUCCESS)
385         return rc;
386
387     if (strcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) == 0)
388     {
389         HKEY key;
390         rc = OpenMediaSubkey(sourcekey, &key, FALSE);
391         if (rc == ERROR_SUCCESS)
392             rc = RegQueryValueExW(key, INSTALLPROPERTY_MEDIAPACKAGEPATHW,
393                     0, 0, (LPBYTE)szValue, pcchValue);
394         if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
395             rc = ERROR_UNKNOWN_PROPERTY;
396         RegCloseKey(key);
397     }
398     else if (strcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW) ==0)
399     {
400         HKEY key;
401         rc = OpenMediaSubkey(sourcekey, &key, FALSE);
402         if (rc == ERROR_SUCCESS)
403             rc = RegQueryValueExW(key, INSTALLPROPERTY_DISKPROMPTW, 0, 0,
404                     (LPBYTE)szValue, pcchValue);
405         if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
406             rc = ERROR_UNKNOWN_PROPERTY;
407         RegCloseKey(key);
408     }
409     else if (strcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW)==0)
410     {
411         LPWSTR buffer;
412         DWORD size = 0;
413
414         RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0,
415                 NULL, &size);
416         if (size == 0)
417             rc = ERROR_UNKNOWN_PROPERTY;
418         else
419         {
420             LPWSTR ptr;
421             buffer = msi_alloc(size);
422             rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
423                     0, 0, (LPBYTE)buffer,&size); 
424             ptr = strchrW(buffer,';');
425             if (ptr) ptr = strchrW(ptr+1,';');
426             if (!ptr)
427                 rc = ERROR_UNKNOWN_PROPERTY;
428             else
429             {
430                 ptr ++;
431                 lstrcpynW(szValue, ptr, *pcchValue);
432                 if (lstrlenW(ptr) > *pcchValue)
433                 {
434                     *pcchValue = lstrlenW(ptr)+1;
435                     rc = ERROR_MORE_DATA;
436                 }
437                 else
438                     rc = ERROR_SUCCESS;
439             }
440             msi_free(buffer);
441         }
442     }
443     else if (strcmpW(INSTALLPROPERTY_LASTUSEDTYPEW, szProperty)==0)
444     {
445         LPWSTR buffer;
446         DWORD size = 0;
447
448         RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0,
449                 NULL, &size);
450         if (size == 0)
451             rc = ERROR_UNKNOWN_PROPERTY;
452         else
453         {
454             buffer = msi_alloc(size);
455             rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
456                     0, 0, (LPBYTE)buffer,&size); 
457             if (*pcchValue < 1)
458             {
459                 rc = ERROR_MORE_DATA;
460                 *pcchValue = 1;
461             }
462             else
463             {
464                 szValue[0] = buffer[0];
465                 rc = ERROR_SUCCESS;
466             }
467             msi_free(buffer);
468         }
469     }
470     else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0)
471     {
472         *pcchValue = *pcchValue * sizeof(WCHAR);
473         rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, 0,
474                               (LPBYTE)szValue, pcchValue);
475         if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
476         {
477             *pcchValue = 0;
478             rc = ERROR_SUCCESS;
479         }
480         else
481         {
482             if (*pcchValue)
483                 *pcchValue = (*pcchValue - 1) / sizeof(WCHAR);
484             if (szValue)
485                 szValue[*pcchValue] = '\0';
486         }
487     }
488     else
489     {
490         FIXME("Unknown property %s\n",debugstr_w(szProperty));
491         rc = ERROR_UNKNOWN_PROPERTY;
492     }
493
494     RegCloseKey(sourcekey);
495     return rc;
496 }
497
498 /******************************************************************
499  *  MsiSourceListSetInfoA   (MSI.@)
500  */
501 UINT WINAPI MsiSourceListSetInfoA(LPCSTR szProduct, LPCSTR szUserSid,
502                                   MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
503                                   LPCSTR szProperty, LPCSTR szValue)
504 {
505     UINT ret;
506     LPWSTR product = NULL;
507     LPWSTR usersid = NULL;
508     LPWSTR property = NULL;
509     LPWSTR value = NULL;
510
511     if (szProduct) product = strdupAtoW(szProduct);
512     if (szUserSid) usersid = strdupAtoW(szUserSid);
513     if (szProperty) property = strdupAtoW(szProperty);
514     if (szValue) value = strdupAtoW(szValue);
515
516     ret = MsiSourceListSetInfoW(product, usersid, dwContext, dwOptions,
517                                 property, value);
518
519     msi_free(product);
520     msi_free(usersid);
521     msi_free(property);
522     msi_free(value);
523
524     return ret;
525 }
526
527 static UINT set_last_used_source(HKEY source, LPCWSTR product, LPCWSTR usersid,
528                                  MSIINSTALLCONTEXT context, DWORD options,
529                                  LPCWSTR value)
530 {
531     LPWSTR buffer;
532     WCHAR typechar;
533     DWORD size;
534     UINT r;
535     int index = 0;
536
537     static const WCHAR format[] = {'%','c',';','%','i',';','%','s',0};
538
539     if (options & MSISOURCETYPE_NETWORK)
540         typechar = 'n';
541     else if (options & MSISOURCETYPE_URL)
542         typechar = 'u';
543     else
544         return ERROR_INVALID_PARAMETER;
545
546     /* make sure the source is registered */
547     r = MsiSourceListAddSourceExW(product, usersid, context,
548                                   options, value, 0);
549     if (r != ERROR_SUCCESS)
550         return r;
551
552     while ((r = MsiSourceListEnumSourcesW(product, usersid, context, options,
553                                           index, NULL, NULL)) == ERROR_SUCCESS)
554         index++;
555
556     if (r != ERROR_NO_MORE_ITEMS)
557         return r;
558
559     size = (lstrlenW(format) + lstrlenW(value) + 7) * sizeof(WCHAR);
560     buffer = msi_alloc(size);
561     if (!buffer)
562         return ERROR_OUTOFMEMORY;
563
564     sprintfW(buffer, format, typechar, index, value);
565
566     size = (lstrlenW(buffer) + 1) * sizeof(WCHAR);
567     r = RegSetValueExW(source, INSTALLPROPERTY_LASTUSEDSOURCEW, 0,
568                        REG_SZ, (LPBYTE)buffer, size);
569     msi_free(buffer);
570
571     return r;
572 }
573
574 /******************************************************************
575  *  MsiSourceListSetInfoW   (MSI.@)
576  */
577 UINT WINAPI MsiSourceListSetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
578                                    MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
579                                    LPCWSTR szProperty, LPCWSTR szValue)
580 {
581     WCHAR squished_pc[GUID_SIZE];
582     HKEY sourcekey, media;
583     LPCWSTR property;
584     UINT rc;
585
586     static const WCHAR media_package[] = {
587         'M','e','d','i','a','P','a','c','k','a','g','e',0
588     };
589
590     TRACE("%s %s %x %x %s %s\n", debugstr_w(szProduct), debugstr_w(szUserSid),
591             dwContext, dwOptions, debugstr_w(szProperty), debugstr_w(szValue));
592
593     if (!szProduct || !squash_guid(szProduct, squished_pc))
594         return ERROR_INVALID_PARAMETER;
595
596     if (!szProperty)
597         return ERROR_INVALID_PARAMETER;
598
599     if (!szValue)
600         return ERROR_UNKNOWN_PROPERTY;
601
602     if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid)
603         return ERROR_INVALID_PARAMETER;
604
605     if (dwOptions & MSICODE_PATCH)
606     {
607         FIXME("Unhandled options MSICODE_PATCH\n");
608         return ERROR_UNKNOWN_PATCH;
609     }
610
611     property = szProperty;
612     if (!lstrcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW))
613         property = media_package;
614
615     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
616     if (rc != ERROR_SUCCESS)
617         return rc;
618
619     if (lstrcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW) &&
620         dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL))
621     {
622         RegCloseKey(sourcekey);
623         return ERROR_INVALID_PARAMETER;
624     }
625
626     if (!lstrcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) ||
627         !lstrcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW))
628     {
629         rc = OpenMediaSubkey(sourcekey, &media, TRUE);
630         if (rc == ERROR_SUCCESS)
631         {
632             rc = msi_reg_set_val_str(media, property, szValue);
633             RegCloseKey(media);
634         }
635     }
636     else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0)
637     {
638         DWORD size = lstrlenW(szValue)*sizeof(WCHAR);
639         rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0,
640                 REG_SZ, (const BYTE *)szValue, size);
641         if (rc != ERROR_SUCCESS)
642             rc = ERROR_UNKNOWN_PROPERTY;
643     }
644     else if (strcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW)==0)
645         rc = set_last_used_source(sourcekey, szProduct, szUserSid, dwContext,
646                                   dwOptions, szValue);
647     else
648         rc = ERROR_UNKNOWN_PROPERTY;
649
650     RegCloseKey(sourcekey);
651     return rc;
652
653 }
654
655 /******************************************************************
656  *  MsiSourceListAddSourceW (MSI.@)
657  */
658 UINT WINAPI MsiSourceListAddSourceW( LPCWSTR szProduct, LPCWSTR szUserName,
659         DWORD dwReserved, LPCWSTR szSource)
660 {
661     INT ret;
662     LPWSTR sidstr = NULL;
663     DWORD sidsize = 0;
664     DWORD domsize = 0;
665
666     TRACE("%s %s %s\n", debugstr_w(szProduct), debugstr_w(szUserName), debugstr_w(szSource));
667
668     if (LookupAccountNameW(NULL, szUserName, NULL, &sidsize, NULL, &domsize, NULL))
669     {
670         PSID psid = msi_alloc(sidsize);
671
672         if (LookupAccountNameW(NULL, szUserName, psid, &sidsize, NULL, &domsize, NULL))
673             ConvertSidToStringSidW(psid, &sidstr);
674
675         msi_free(psid);
676     }
677
678     ret = MsiSourceListAddSourceExW(szProduct, sidstr, 
679         MSIINSTALLCONTEXT_USERMANAGED, MSISOURCETYPE_NETWORK, szSource, 0);
680
681     if (sidstr)
682         LocalFree(sidstr);
683
684     return ret;
685 }
686
687 /******************************************************************
688  *  MsiSourceListAddSourceA (MSI.@)
689  */
690 UINT WINAPI MsiSourceListAddSourceA( LPCSTR szProduct, LPCSTR szUserName,
691         DWORD dwReserved, LPCSTR szSource)
692 {
693     INT ret;
694     LPWSTR szwproduct;
695     LPWSTR szwusername;
696     LPWSTR szwsource;
697
698     szwproduct = strdupAtoW( szProduct );
699     szwusername = strdupAtoW( szUserName );
700     szwsource = strdupAtoW( szSource );
701
702     ret = MsiSourceListAddSourceW(szwproduct, szwusername, 0, szwsource);
703
704     msi_free(szwproduct);
705     msi_free(szwusername);
706     msi_free(szwsource);
707
708     return ret;
709 }
710
711 /******************************************************************
712  *  MsiSourceListAddSourceExA (MSI.@)
713  */
714 UINT WINAPI MsiSourceListAddSourceExA(LPCSTR szProduct, LPCSTR szUserSid,
715         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCSTR szSource, DWORD dwIndex)
716 {
717     UINT ret;
718     LPWSTR product, usersid, source;
719
720     product = strdupAtoW(szProduct);
721     usersid = strdupAtoW(szUserSid);
722     source = strdupAtoW(szSource);
723
724     ret = MsiSourceListAddSourceExW(product, usersid, dwContext,
725                                     dwOptions, source, dwIndex);
726
727     msi_free(product);
728     msi_free(usersid);
729     msi_free(source);
730
731     return ret;
732 }
733
734 static void free_source_list(struct list *sourcelist)
735 {
736     while (!list_empty(sourcelist))
737     {
738         media_info *info = LIST_ENTRY(list_head(sourcelist), media_info, entry);
739         list_remove(&info->entry);
740         msi_free(info->path);
741         msi_free(info);
742     }
743 }
744
745 static void add_source_to_list(struct list *sourcelist, media_info *info)
746 {
747     media_info *iter;
748     BOOL found = FALSE;
749     static const WCHAR fmt[] = {'%','i',0};
750
751     if (list_empty(sourcelist))
752     {
753         list_add_head(sourcelist, &info->entry);
754         return;
755     }
756
757     LIST_FOR_EACH_ENTRY(iter, sourcelist, media_info, entry)
758     {
759         if (!found && info->index < iter->index)
760         {
761             found = TRUE;
762             list_add_before(&iter->entry, &info->entry);
763         }
764
765         /* update the rest of the list */
766         if (found)
767             sprintfW(iter->szIndex, fmt, ++iter->index);
768     }
769
770     if (!found)
771         list_add_after(&iter->entry, &info->entry);
772 }
773
774 static UINT fill_source_list(struct list *sourcelist, HKEY sourcekey, DWORD *count)
775 {
776     UINT r = ERROR_SUCCESS;
777     DWORD index = 0;
778     WCHAR name[10];
779     DWORD size, val_size;
780     media_info *entry;
781
782     *count = 0;
783
784     while (r == ERROR_SUCCESS)
785     {
786         size = sizeof(name) / sizeof(name[0]);
787         r = RegEnumValueW(sourcekey, index, name, &size, NULL, NULL, NULL, &val_size);
788         if (r != ERROR_SUCCESS)
789             return r;
790
791         entry = msi_alloc(sizeof(media_info));
792         if (!entry)
793             goto error;
794
795         entry->path = msi_alloc(val_size);
796         if (!entry->path)
797         {
798             msi_free(entry);
799             goto error;
800         }
801
802         lstrcpyW(entry->szIndex, name);
803         entry->index = atoiW(name);
804
805         size++;
806         r = RegEnumValueW(sourcekey, index, name, &size, NULL,
807                           NULL, (LPBYTE)entry->path, &val_size);
808         if (r != ERROR_SUCCESS)
809         {
810             msi_free(entry->path);
811             msi_free(entry);
812             goto error;
813         }
814
815         index = ++(*count);
816         add_source_to_list(sourcelist, entry);
817     }
818
819 error:
820     *count = -1;
821     free_source_list(sourcelist);
822     return ERROR_OUTOFMEMORY;
823 }
824
825 /******************************************************************
826  *  MsiSourceListAddSourceExW (MSI.@)
827  */
828 UINT WINAPI MsiSourceListAddSourceExW( LPCWSTR szProduct, LPCWSTR szUserSid,
829         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szSource, 
830         DWORD dwIndex)
831 {
832     HKEY sourcekey;
833     HKEY typekey;
834     UINT rc;
835     struct list sourcelist;
836     media_info *info;
837     WCHAR squished_pc[GUID_SIZE];
838     WCHAR name[10];
839     LPWSTR source;
840     LPCWSTR postfix;
841     DWORD size, count;
842
843     static const WCHAR fmt[] = {'%','i',0};
844     static const WCHAR one[] = {'1',0};
845     static const WCHAR backslash[] = {'\\',0};
846     static const WCHAR forwardslash[] = {'/',0};
847
848     TRACE("%s %s %x %x %s %i\n", debugstr_w(szProduct), debugstr_w(szUserSid),
849           dwContext, dwOptions, debugstr_w(szSource), dwIndex);
850
851     if (!szProduct || !squash_guid(szProduct, squished_pc))
852         return ERROR_INVALID_PARAMETER;
853
854     if (!szSource || !*szSource)
855         return ERROR_INVALID_PARAMETER;
856
857     if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
858         return ERROR_INVALID_PARAMETER;
859
860     if (dwOptions & MSICODE_PATCH)
861     {
862         FIXME("Unhandled options MSICODE_PATCH\n");
863         return ERROR_FUNCTION_FAILED;
864     }
865
866     if (szUserSid && (dwContext & MSIINSTALLCONTEXT_MACHINE))
867         return ERROR_INVALID_PARAMETER;
868
869     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
870     if (rc != ERROR_SUCCESS)
871         return rc;
872
873     if (dwOptions & MSISOURCETYPE_NETWORK)
874         rc = OpenNetworkSubkey(sourcekey, &typekey, TRUE);
875     else if (dwOptions & MSISOURCETYPE_URL)
876         rc = OpenURLSubkey(sourcekey, &typekey, TRUE);
877     else if (dwOptions & MSISOURCETYPE_MEDIA)
878         rc = OpenMediaSubkey(sourcekey, &typekey, TRUE);
879     else
880     {
881         ERR("unknown media type: %08x\n", dwOptions);
882         RegCloseKey(sourcekey);
883         return ERROR_FUNCTION_FAILED;
884     }
885
886     postfix = (dwOptions & MSISOURCETYPE_NETWORK) ? backslash : forwardslash;
887     if (szSource[lstrlenW(szSource) - 1] == *postfix)
888         source = strdupW(szSource);
889     else
890     {
891         size = lstrlenW(szSource) + 2;
892         source = msi_alloc(size * sizeof(WCHAR));
893         lstrcpyW(source, szSource);
894         lstrcatW(source, postfix);
895     }
896
897     list_init(&sourcelist);
898     rc = fill_source_list(&sourcelist, typekey, &count);
899     if (rc != ERROR_NO_MORE_ITEMS)
900         return rc;
901
902     size = (lstrlenW(source) + 1) * sizeof(WCHAR);
903
904     if (count == 0)
905     {
906         rc = RegSetValueExW(typekey, one, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
907         goto done;
908     }
909     else if (dwIndex > count)
910     {
911         sprintfW(name, fmt, count + 1);
912         rc = RegSetValueExW(typekey, name, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
913         goto done;
914     }
915     else
916     {
917         /* add to the end of the list */
918         if (dwIndex == 0)
919             dwIndex = count + 1;
920
921         sprintfW(name, fmt, dwIndex);
922         info = msi_alloc(sizeof(media_info));
923         if (!info)
924         {
925             rc = ERROR_OUTOFMEMORY;
926             goto done;
927         }
928
929         info->path = strdupW(source);
930         lstrcpyW(info->szIndex, name);
931         info->index = dwIndex;
932         add_source_to_list(&sourcelist, info);
933
934         LIST_FOR_EACH_ENTRY(info, &sourcelist, media_info, entry)
935         {
936             size = (lstrlenW(info->path) + 1) * sizeof(WCHAR);
937             rc = RegSetValueExW(typekey, info->szIndex, 0,
938                                 REG_EXPAND_SZ, (LPBYTE)info->path, size);
939             if (rc != ERROR_SUCCESS)
940                 goto done;
941         }
942     }
943
944 done:
945     free_source_list(&sourcelist);
946     msi_free(source);
947     RegCloseKey(typekey);
948     RegCloseKey(sourcekey);
949     return rc;
950 }
951
952 /******************************************************************
953  *  MsiSourceListAddMediaDisk(MSI.@)
954  */
955 UINT WINAPI MsiSourceListAddMediaDiskW(LPCWSTR szProduct, LPCWSTR szUserSid, 
956         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId, 
957         LPCWSTR szVolumeLabel, LPCWSTR szDiskPrompt)
958 {
959     HKEY sourcekey;
960     HKEY mediakey;
961     UINT rc;
962     WCHAR szIndex[10];
963     static const WCHAR fmt[] = {'%','i',0};
964     static const WCHAR disk_fmt[] = {'%','s',';','%','s',0};
965     static const WCHAR empty[1] = {0};
966     LPCWSTR pt1,pt2;
967     LPWSTR buffer;
968     DWORD size;
969
970     TRACE("%s %s %x %x %i %s %s\n", debugstr_w(szProduct),
971             debugstr_w(szUserSid), dwContext, dwOptions, dwDiskId,
972             debugstr_w(szVolumeLabel), debugstr_w(szDiskPrompt));
973
974     if (!szProduct || lstrlenW(szProduct) > 39)
975         return ERROR_INVALID_PARAMETER;
976
977     if (dwOptions & MSICODE_PATCH)
978     {
979         FIXME("Unhandled options MSICODE_PATCH\n");
980         return ERROR_FUNCTION_FAILED;
981     }
982     
983     if (szUserSid)
984         FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
985
986     if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
987         FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
988
989     rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, TRUE);
990     if (rc != ERROR_SUCCESS)
991         return ERROR_UNKNOWN_PRODUCT;
992
993     OpenMediaSubkey(sourcekey,&mediakey,TRUE);
994
995     sprintfW(szIndex,fmt,dwDiskId);
996
997     size = 2;
998     if (szVolumeLabel)
999     {
1000         size +=lstrlenW(szVolumeLabel);
1001         pt1 = szVolumeLabel;
1002     }
1003     else
1004         pt1 = empty;
1005     if (szDiskPrompt)
1006     {
1007         size +=lstrlenW(szDiskPrompt);
1008         pt2 = szDiskPrompt;
1009     }
1010     else
1011         pt2 = empty;
1012
1013     size *=sizeof(WCHAR);
1014
1015     buffer = msi_alloc(size);
1016     sprintfW(buffer,disk_fmt,pt1,pt2);
1017
1018     RegSetValueExW(mediakey, szIndex, 0, REG_SZ, (LPBYTE)buffer, size);
1019     msi_free( buffer );
1020
1021     RegCloseKey(sourcekey);
1022     RegCloseKey(mediakey);
1023
1024     return ERROR_SUCCESS;
1025 }
1026
1027 /******************************************************************
1028  *  MsiSourceListClearAllA (MSI.@)
1029  */
1030 UINT WINAPI MsiSourceListClearAllA( LPCSTR szProduct, LPCSTR szUserName, DWORD dwReserved )
1031 {
1032     FIXME("(%s %s %d)\n", debugstr_a(szProduct), debugstr_a(szUserName), dwReserved);
1033     return ERROR_SUCCESS;
1034 }
1035
1036 /******************************************************************
1037  *  MsiSourceListClearAllW (MSI.@)
1038  */
1039 UINT WINAPI MsiSourceListClearAllW( LPCWSTR szProduct, LPCWSTR szUserName, DWORD dwReserved )
1040 {
1041     FIXME("(%s %s %d)\n", debugstr_w(szProduct), debugstr_w(szUserName), dwReserved);
1042     return ERROR_SUCCESS;
1043 }