msi: Add a test for MsiGetComponentPath and make it pass.
[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 "action.h"
40 #include "sddl.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(msi);
43
44 /*
45  * These apis are defined in MSI 3.0
46  */
47
48 typedef struct tagMediaInfo
49 {
50     LPWSTR  path;
51     WCHAR   szIndex[10];
52     WCHAR   type;
53 } media_info;
54
55 static UINT OpenSourceKey(LPCWSTR szProduct, HKEY* key, BOOL user, BOOL create)
56 {
57     HKEY rootkey = 0; 
58     UINT rc; 
59     static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
60
61     if (user)
62         rc = MSIREG_OpenUserProductsKey(szProduct, &rootkey, create);
63     else
64         rc = MSIREG_OpenProductsKey(szProduct, &rootkey, create);
65
66     if (rc)
67         return rc;
68
69     if (create)
70         rc = RegCreateKeyW(rootkey, szSourceList, key);
71     else
72         rc = RegOpenKeyW(rootkey,szSourceList, key); 
73
74     return rc;
75 }
76
77 static UINT OpenMediaSubkey(HKEY rootkey, HKEY *key, BOOL create)
78 {
79     UINT rc;
80     static const WCHAR media[] = {'M','e','d','i','a',0};
81
82     if (create)
83         rc = RegCreateKeyW(rootkey, media, key);
84     else
85         rc = RegOpenKeyW(rootkey,media, key); 
86
87     return rc;
88 }
89
90 static UINT OpenNetworkSubkey(HKEY rootkey, HKEY *key, BOOL create)
91 {
92     UINT rc;
93     static const WCHAR net[] = {'N','e','t',0};
94
95     if (create)
96         rc = RegCreateKeyW(rootkey, net, key);
97     else
98         rc = RegOpenKeyW(rootkey, net, key); 
99
100     return rc;
101 }
102
103 static UINT OpenURLSubkey(HKEY rootkey, HKEY *key, BOOL create)
104 {
105     UINT rc;
106     static const WCHAR URL[] = {'U','R','L',0};
107
108     if (create)
109         rc = RegCreateKeyW(rootkey, URL, key);
110     else
111         rc = RegOpenKeyW(rootkey, URL, key); 
112
113     return rc;
114 }
115
116
117 static UINT find_given_source(HKEY key, LPCWSTR szSource, media_info *ss)
118 {
119     DWORD index = 0;
120     WCHAR szIndex[10];
121     DWORD size;
122     DWORD val_size;
123     LPWSTR val;
124     UINT rc = ERROR_SUCCESS;
125
126     while (rc == ERROR_SUCCESS)
127     {
128         val = NULL;
129         val_size = 0;
130         size = sizeof(szIndex)/sizeof(szIndex[0]);
131         rc = RegEnumValueW(key, index, szIndex, &size, NULL, NULL, NULL, &val_size);
132         if (rc != ERROR_NO_MORE_ITEMS)
133         {
134             val = msi_alloc(val_size);
135             RegEnumValueW(key, index, szIndex, &size, NULL, NULL, (LPBYTE)val, 
136                 &val_size);
137             if (lstrcmpiW(szSource,val)==0)
138             {
139                 ss->path = val;
140                 strcpyW(ss->szIndex,szIndex);
141                 break;
142             }
143             else
144                 strcpyW(ss->szIndex,szIndex);
145
146             msi_free(val);
147             index ++;
148         }
149     }
150     return rc;
151 }
152
153 /******************************************************************
154  *  MsiSourceListGetInfoW   (MSI.@)
155  */
156 UINT WINAPI MsiSourceListGetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
157                                    MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
158                                    LPCWSTR szProperty, LPWSTR szValue, 
159                                    LPDWORD pcchValue) 
160 {
161     HKEY sourcekey;
162     UINT rc;
163
164     TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szProperty));
165
166     if (!szProduct || lstrlenW(szProduct) > 39)
167         return ERROR_INVALID_PARAMETER;
168
169     if (szValue && !pcchValue)
170         return ERROR_INVALID_PARAMETER;
171     
172     if (dwOptions == MSICODE_PATCH)
173     {
174         FIXME("Unhandled options MSICODE_PATCH\n");
175         return ERROR_FUNCTION_FAILED;
176     }
177     
178     if (szUserSid)
179         FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
180
181     if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
182         FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
183
184     if (dwContext == MSIINSTALLCONTEXT_MACHINE)
185         rc = OpenSourceKey(szProduct, &sourcekey, FALSE, FALSE);
186     else
187         rc = OpenSourceKey(szProduct, &sourcekey, TRUE, FALSE);
188
189     if (rc != ERROR_SUCCESS)
190         return ERROR_UNKNOWN_PRODUCT;
191
192     if (strcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) == 0)
193     {
194         HKEY key;
195         rc = OpenMediaSubkey(sourcekey, &key, FALSE);
196         if (rc == ERROR_SUCCESS)
197             rc = RegQueryValueExW(key, INSTALLPROPERTY_MEDIAPACKAGEPATHW,
198                     0, 0, (LPBYTE)szValue, pcchValue);
199         if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
200             rc = ERROR_UNKNOWN_PROPERTY;
201         RegCloseKey(key);
202     }
203     else if (strcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW) ==0)
204     {
205         HKEY key;
206         rc = OpenMediaSubkey(sourcekey, &key, FALSE);
207         if (rc == ERROR_SUCCESS)
208             rc = RegQueryValueExW(key, INSTALLPROPERTY_DISKPROMPTW, 0, 0,
209                     (LPBYTE)szValue, pcchValue);
210         if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
211             rc = ERROR_UNKNOWN_PROPERTY;
212         RegCloseKey(key);
213     }
214     else if (strcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW)==0)
215     {
216         LPWSTR buffer;
217         DWORD size = 0;
218
219         RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0,
220                 NULL, &size);
221         if (size == 0)
222             rc = ERROR_UNKNOWN_PROPERTY;
223         else
224         {
225             LPWSTR ptr;
226             buffer = msi_alloc(size);
227             rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
228                     0, 0, (LPBYTE)buffer,&size); 
229             ptr = strchrW(buffer,';');
230             if (ptr) ptr = strchrW(ptr+1,';');
231             if (!ptr)
232                 rc = ERROR_UNKNOWN_PROPERTY;
233             else
234             {
235                 ptr ++;
236                 lstrcpynW(szValue, ptr, *pcchValue);
237                 if (lstrlenW(ptr) > *pcchValue)
238                 {
239                     *pcchValue = lstrlenW(ptr)+1;
240                     rc = ERROR_MORE_DATA;
241                 }
242                 else
243                     rc = ERROR_SUCCESS;
244             }
245             msi_free(buffer);
246         }
247     }
248     else if (strcmpW(INSTALLPROPERTY_LASTUSEDTYPEW, szProperty)==0)
249     {
250         LPWSTR buffer;
251         DWORD size = 0;
252
253         RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0,
254                 NULL, &size);
255         if (size == 0)
256             rc = ERROR_UNKNOWN_PROPERTY;
257         else
258         {
259             buffer = msi_alloc(size);
260             rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
261                     0, 0, (LPBYTE)buffer,&size); 
262             if (*pcchValue < 1)
263             {
264                 rc = ERROR_MORE_DATA;
265                 *pcchValue = 1;
266             }
267             else
268             {
269                 szValue[0] = buffer[0];
270                 rc = ERROR_SUCCESS;
271             }
272             msi_free(buffer);
273         }
274     }
275     else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0)
276     {
277         rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, 0, 
278                 (LPBYTE)szValue, pcchValue);
279         if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
280             rc = ERROR_UNKNOWN_PROPERTY;
281     }
282     else
283     {
284         FIXME("Unknown property %s\n",debugstr_w(szProperty));
285         rc = ERROR_UNKNOWN_PROPERTY;
286     }
287
288     RegCloseKey(sourcekey);
289     return rc;
290 }
291
292 /******************************************************************
293  *  MsiSourceListSetInfoW   (MSI.@)
294  */
295 UINT WINAPI MsiSourceListSetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
296                                    MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
297                                    LPCWSTR szProperty, LPCWSTR szValue)
298 {
299     HKEY sourcekey;
300     UINT rc;
301
302     TRACE("%s %s %x %lx %s %s\n", debugstr_w(szProduct), debugstr_w(szUserSid), 
303             dwContext, dwOptions, debugstr_w(szProperty), debugstr_w(szValue));
304
305     if (!szProduct || lstrlenW(szProduct) > 39)
306         return ERROR_INVALID_PARAMETER;
307
308     if (dwOptions & MSICODE_PATCH)
309     {
310         FIXME("Unhandled options MSICODE_PATCH\n");
311         return ERROR_FUNCTION_FAILED;
312     }
313     
314     if (szUserSid)
315         FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
316
317     if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
318         FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
319
320     if (dwContext == MSIINSTALLCONTEXT_MACHINE)
321         rc = OpenSourceKey(szProduct, &sourcekey, FALSE, TRUE);
322     else
323         rc = OpenSourceKey(szProduct, &sourcekey, TRUE, TRUE);
324
325     if (rc != ERROR_SUCCESS)
326         return ERROR_UNKNOWN_PRODUCT;
327
328
329     if (strcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) == 0)
330     {
331         HKEY key;
332         DWORD size = lstrlenW(szValue)*sizeof(WCHAR);
333         rc = OpenMediaSubkey(sourcekey, &key, FALSE);
334         if (rc == ERROR_SUCCESS)
335             rc = RegSetValueExW(key, INSTALLPROPERTY_MEDIAPACKAGEPATHW, 0,
336                     REG_SZ, (LPBYTE)szValue, size);
337         if (rc != ERROR_SUCCESS)
338             rc = ERROR_UNKNOWN_PROPERTY;
339         RegCloseKey(key);
340     }
341     else if (strcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW) == 0)
342     {
343         HKEY key;
344         DWORD size = lstrlenW(szValue)*sizeof(WCHAR);
345         rc = OpenMediaSubkey(sourcekey, &key, FALSE);
346         if (rc == ERROR_SUCCESS)
347             rc = RegSetValueExW(key, INSTALLPROPERTY_DISKPROMPTW, 0,
348                     REG_SZ, (LPBYTE)szValue, size);
349         if (rc != ERROR_SUCCESS)
350             rc = ERROR_UNKNOWN_PROPERTY;
351         RegCloseKey(key);
352     }
353     else if (strcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW)==0)
354     {
355         LPWSTR buffer = NULL;
356         DWORD size;
357         WCHAR typechar = 'n';
358         static const WCHAR LastUsedSource_Fmt[] = {'%','c',';','%','i',';','%','s',0};
359
360         /* make sure the source is registered */
361         MsiSourceListAddSourceExW(szProduct, szUserSid, dwContext, 
362                 dwOptions, szValue, 0); 
363
364         if (dwOptions & MSISOURCETYPE_NETWORK)
365             typechar = 'n';
366         else if (dwOptions & MSISOURCETYPE_URL)
367             typechar = 'u';
368         else if (dwOptions & MSISOURCETYPE_MEDIA)
369             typechar = 'm';
370         else 
371             ERR("Unknown source type! 0x%lx\n",dwOptions);
372         
373         size = (lstrlenW(szValue)+5)*sizeof(WCHAR);
374         buffer = msi_alloc(size);
375         sprintfW(buffer, LastUsedSource_Fmt, typechar, 1, szValue);
376         rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 
377                 REG_EXPAND_SZ, (LPBYTE)buffer, size);
378         if (rc != ERROR_SUCCESS)
379             rc = ERROR_UNKNOWN_PROPERTY;
380         msi_free( buffer );
381     }
382     else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0)
383     {
384         DWORD size = lstrlenW(szValue)*sizeof(WCHAR);
385         rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0,
386                 REG_SZ, (LPBYTE)szValue, size);
387         if (rc != ERROR_SUCCESS)
388             rc = ERROR_UNKNOWN_PROPERTY;
389     }
390     else
391     {
392         FIXME("Unknown property %s\n",debugstr_w(szProperty));
393         rc = ERROR_UNKNOWN_PROPERTY;
394     }
395
396     RegCloseKey(sourcekey);
397     return rc;
398
399 }
400
401 /******************************************************************
402  *  MsiSourceListAddSourceW (MSI.@)
403  */
404 UINT WINAPI MsiSourceListAddSourceW( LPCWSTR szProduct, LPCWSTR szUserName,
405         DWORD dwReserved, LPCWSTR szSource)
406 {
407     INT ret;
408     LPWSTR sidstr = NULL;
409     DWORD sidsize = 0;
410
411     TRACE("%s %s %s\n", debugstr_w(szProduct), debugstr_w(szUserName), debugstr_w(szSource));
412
413     if (LookupAccountNameW(NULL, szUserName, NULL, &sidsize, NULL, NULL, NULL))
414     {
415         PSID psid = msi_alloc(sidsize);
416
417         if (LookupAccountNameW(NULL, szUserName, psid, &sidsize, NULL, NULL, NULL))
418             ConvertSidToStringSidW(psid, &sidstr);
419
420         msi_free(psid);
421     }
422
423     ret = MsiSourceListAddSourceExW(szProduct, sidstr, 
424         MSIINSTALLCONTEXT_USERMANAGED, MSISOURCETYPE_NETWORK, szSource, 0);
425
426     if (sidstr)
427         LocalFree(sidstr);
428
429     return ret;
430 }
431
432 /******************************************************************
433  *  MsiSourceListAddSourceA (MSI.@)
434  */
435 UINT WINAPI MsiSourceListAddSourceA( LPCSTR szProduct, LPCSTR szUserName,
436         DWORD dwReserved, LPCSTR szSource)
437 {
438     INT ret;
439     LPWSTR szwproduct;
440     LPWSTR szwusername;
441     LPWSTR szwsource;
442
443     szwproduct = strdupAtoW( szProduct );
444     szwusername = strdupAtoW( szUserName );
445     szwsource = strdupAtoW( szSource );
446
447     ret = MsiSourceListAddSourceW(szwproduct, szwusername, 0, szwsource);
448
449     msi_free(szwproduct);
450     msi_free(szwusername);
451     msi_free(szwsource);
452
453     return ret;
454 }
455
456 /******************************************************************
457  *  MsiSourceListAddSourceExW (MSI.@)
458  */
459 UINT WINAPI MsiSourceListAddSourceExW( LPCWSTR szProduct, LPCWSTR szUserSid,
460         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szSource, 
461         DWORD dwIndex)
462 {
463     HKEY sourcekey;
464     HKEY typekey;
465     UINT rc;
466     media_info source_struct;
467    
468     TRACE("%s, %s, %x, %lx, %s, %li\n", debugstr_w(szProduct), 
469             debugstr_w(szUserSid), dwContext, dwOptions, debugstr_w(szSource), 
470             dwIndex);
471     
472     if (!szProduct)
473         return ERROR_INVALID_PARAMETER;
474
475     if (!szSource)
476         return ERROR_INVALID_PARAMETER;
477
478     if (dwOptions & MSICODE_PATCH)
479     {
480         FIXME("Unhandled options MSICODE_PATCH\n");
481         return ERROR_FUNCTION_FAILED;
482     }
483
484     if (szUserSid)
485         FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
486
487     if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
488         FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
489
490     if (dwContext == MSIINSTALLCONTEXT_MACHINE)
491         rc = OpenSourceKey(szProduct, &sourcekey, FALSE, TRUE);
492     else
493         rc = OpenSourceKey(szProduct, &sourcekey, TRUE, TRUE);
494
495     if (rc != ERROR_SUCCESS)
496         return ERROR_UNKNOWN_PRODUCT;
497
498     if (dwOptions & MSISOURCETYPE_NETWORK)
499         rc = OpenNetworkSubkey(sourcekey, &typekey, TRUE);
500     else if (dwOptions & MSISOURCETYPE_URL)
501         rc = OpenURLSubkey(sourcekey, &typekey, TRUE);
502     else if (dwOptions & MSISOURCETYPE_MEDIA)
503         rc = OpenMediaSubkey(sourcekey, &typekey, TRUE);
504     else
505     {
506         ERR("unknown media type: %08lx\n", dwOptions);
507         RegCloseKey(sourcekey);
508         return ERROR_FUNCTION_FAILED;
509     }
510
511     source_struct.szIndex[0] = 0;
512     if (find_given_source(typekey, szSource, &source_struct)==ERROR_SUCCESS)
513     {
514         DWORD current_index = atoiW(source_struct.szIndex);
515         /* found the source */
516         if (dwIndex > 0 && current_index != dwIndex)
517             FIXME("Need to reorder the sources!\n");
518     }
519     else
520     {
521         DWORD current_index = 0;
522         static const WCHAR fmt[] = {'%','i',0};
523         DWORD size = lstrlenW(szSource)*sizeof(WCHAR);
524
525         if (source_struct.szIndex[0])
526             current_index = atoiW(source_struct.szIndex);
527         /* new source */
528         if (dwIndex > 0 && dwIndex < current_index)
529             FIXME("Need to reorder the sources!\n");
530
531         current_index ++;
532         sprintfW(source_struct.szIndex,fmt,current_index);
533         rc = RegSetValueExW(typekey, source_struct.szIndex, 0, REG_EXPAND_SZ, 
534                 (LPBYTE)szSource, size);
535     }
536
537     RegCloseKey(typekey);
538     RegCloseKey(sourcekey);
539     return rc;
540 }
541
542 /******************************************************************
543  *  MsiSourceListAddMediaDisk(MSI.@)
544  */
545 UINT WINAPI MsiSourceListAddMediaDiskW(LPCWSTR szProduct, LPCWSTR szUserSid, 
546         MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId, 
547         LPCWSTR szVolumeLabel, LPCWSTR szDiskPrompt)
548 {
549     HKEY sourcekey;
550     HKEY mediakey;
551     UINT rc;
552     WCHAR szIndex[10];
553     static const WCHAR fmt[] = {'%','i',0};
554     static const WCHAR disk_fmt[] = {'%','s',';','%','s',0};
555     static const WCHAR empty[1] = {0};
556     LPCWSTR pt1,pt2;
557     LPWSTR buffer;
558     DWORD size;
559
560     TRACE("%s %s %x %lx %li %s %s\n", debugstr_w(szProduct), 
561             debugstr_w(szUserSid), dwContext, dwOptions, dwDiskId, 
562             debugstr_w(szVolumeLabel), debugstr_w(szDiskPrompt)); 
563
564     if (!szProduct || lstrlenW(szProduct) > 39)
565         return ERROR_INVALID_PARAMETER;
566
567     if (dwOptions & MSICODE_PATCH)
568     {
569         FIXME("Unhandled options MSICODE_PATCH\n");
570         return ERROR_FUNCTION_FAILED;
571     }
572     
573     if (szUserSid)
574         FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
575
576     if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
577         FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
578
579     if (dwContext == MSIINSTALLCONTEXT_MACHINE)
580         rc = OpenSourceKey(szProduct, &sourcekey, FALSE, TRUE);
581     else
582         rc = OpenSourceKey(szProduct, &sourcekey, TRUE, TRUE);
583
584     if (rc != ERROR_SUCCESS)
585         return ERROR_UNKNOWN_PRODUCT;
586
587     OpenMediaSubkey(sourcekey,&mediakey,TRUE);
588
589     sprintfW(szIndex,fmt,dwDiskId);
590
591     size = 2;
592     if (szVolumeLabel)
593     {
594         size +=lstrlenW(szVolumeLabel);
595         pt1 = szVolumeLabel;
596     }
597     else
598         pt1 = empty;
599     if (szDiskPrompt)
600     {
601         size +=lstrlenW(szDiskPrompt);
602         pt2 = szDiskPrompt;
603     }
604     else
605         pt2 = empty;
606
607     size *=sizeof(WCHAR);
608
609     buffer = msi_alloc(size);
610     sprintfW(buffer,disk_fmt,pt1,pt2);
611
612     RegSetValueExW(mediakey, szIndex, 0, REG_SZ, (LPBYTE)buffer, size);
613     msi_free( buffer );
614
615     RegCloseKey(sourcekey);
616     RegCloseKey(mediakey);
617
618     return ERROR_SUCCESS;
619 }