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