Sync with trunk r64509.
[reactos.git] / dll / win32 / 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 "msipriv.h"
22
23 WINE_DEFAULT_DEBUG_CHANNEL(msi);
24
25 /*
26 * These apis are defined in MSI 3.0
27 */
28
29 typedef struct tagMediaInfo
30 {
31 struct list entry;
32 LPWSTR path;
33 WCHAR szIndex[10];
34 DWORD index;
35 } media_info;
36
37 static UINT OpenSourceKey(LPCWSTR szProduct, HKEY* key, DWORD dwOptions,
38 MSIINSTALLCONTEXT context, BOOL create)
39 {
40 HKEY rootkey = 0;
41 UINT rc = ERROR_FUNCTION_FAILED;
42
43 if (context == MSIINSTALLCONTEXT_USERUNMANAGED)
44 {
45 if (dwOptions & MSICODE_PATCH)
46 rc = MSIREG_OpenUserPatchesKey(szProduct, &rootkey, create);
47 else
48 rc = MSIREG_OpenProductKey(szProduct, NULL, context,
49 &rootkey, create);
50 }
51 else if (context == MSIINSTALLCONTEXT_USERMANAGED)
52 {
53 if (dwOptions & MSICODE_PATCH)
54 rc = MSIREG_OpenUserPatchesKey(szProduct, &rootkey, create);
55 else
56 rc = MSIREG_OpenProductKey(szProduct, NULL, context,
57 &rootkey, create);
58 }
59 else if (context == MSIINSTALLCONTEXT_MACHINE)
60 {
61 if (dwOptions & MSICODE_PATCH)
62 rc = MSIREG_OpenPatchesKey(szProduct, &rootkey, create);
63 else
64 rc = MSIREG_OpenProductKey(szProduct, NULL, context,
65 &rootkey, create);
66 }
67
68 if (rc != ERROR_SUCCESS)
69 {
70 if (dwOptions & MSICODE_PATCH)
71 return ERROR_UNKNOWN_PATCH;
72 else
73 return ERROR_UNKNOWN_PRODUCT;
74 }
75
76 if (create)
77 rc = RegCreateKeyW(rootkey, szSourceList, key);
78 else
79 {
80 rc = RegOpenKeyW(rootkey,szSourceList, key);
81 if (rc != ERROR_SUCCESS)
82 rc = ERROR_BAD_CONFIGURATION;
83 }
84
85 return rc;
86 }
87
88 static UINT OpenMediaSubkey(HKEY rootkey, HKEY *key, BOOL create)
89 {
90 UINT rc;
91 static const WCHAR media[] = {'M','e','d','i','a',0};
92
93 if (create)
94 rc = RegCreateKeyW(rootkey, media, key);
95 else
96 rc = RegOpenKeyW(rootkey,media, key);
97
98 return rc;
99 }
100
101 static UINT OpenNetworkSubkey(HKEY rootkey, HKEY *key, BOOL create)
102 {
103 UINT rc;
104 static const WCHAR net[] = {'N','e','t',0};
105
106 if (create)
107 rc = RegCreateKeyW(rootkey, net, key);
108 else
109 rc = RegOpenKeyW(rootkey, net, key);
110
111 return rc;
112 }
113
114 static UINT OpenURLSubkey(HKEY rootkey, HKEY *key, BOOL create)
115 {
116 UINT rc;
117 static const WCHAR URL[] = {'U','R','L',0};
118
119 if (create)
120 rc = RegCreateKeyW(rootkey, URL, key);
121 else
122 rc = RegOpenKeyW(rootkey, URL, key);
123
124 return rc;
125 }
126
127 /******************************************************************
128 * MsiSourceListEnumMediaDisksA (MSI.@)
129 */
130 UINT WINAPI MsiSourceListEnumMediaDisksA(LPCSTR szProductCodeOrPatchCode,
131 LPCSTR szUserSid, MSIINSTALLCONTEXT dwContext,
132 DWORD dwOptions, DWORD dwIndex, LPDWORD pdwDiskId,
133 LPSTR szVolumeLabel, LPDWORD pcchVolumeLabel,
134 LPSTR szDiskPrompt, LPDWORD pcchDiskPrompt)
135 {
136 LPWSTR product = NULL;
137 LPWSTR usersid = NULL;
138 LPWSTR volume = NULL;
139 LPWSTR prompt = NULL;
140 UINT r = ERROR_INVALID_PARAMETER;
141
142 TRACE("(%s, %s, %d, %d, %d, %p, %p, %p, %p, %p)\n", debugstr_a(szProductCodeOrPatchCode),
143 debugstr_a(szUserSid), dwContext, dwOptions, dwIndex, pdwDiskId,
144 szVolumeLabel, pcchVolumeLabel, szDiskPrompt, pcchDiskPrompt);
145
146 if (szDiskPrompt && !pcchDiskPrompt)
147 return ERROR_INVALID_PARAMETER;
148
149 if (szProductCodeOrPatchCode) product = strdupAtoW(szProductCodeOrPatchCode);
150 if (szUserSid) usersid = strdupAtoW(szUserSid);
151
152 /* FIXME: add tests for an invalid format */
153
154 if (pcchVolumeLabel)
155 volume = msi_alloc(*pcchVolumeLabel * sizeof(WCHAR));
156
157 if (pcchDiskPrompt)
158 prompt = msi_alloc(*pcchDiskPrompt * sizeof(WCHAR));
159
160 if (volume) *volume = '\0';
161 if (prompt) *prompt = '\0';
162 r = MsiSourceListEnumMediaDisksW(product, usersid, dwContext, dwOptions,
163 dwIndex, pdwDiskId, volume, pcchVolumeLabel,
164 prompt, pcchDiskPrompt);
165 if (r != ERROR_SUCCESS)
166 goto done;
167
168 if (szVolumeLabel && pcchVolumeLabel)
169 WideCharToMultiByte(CP_ACP, 0, volume, -1, szVolumeLabel,
170 *pcchVolumeLabel + 1, NULL, NULL);
171
172 if (szDiskPrompt)
173 WideCharToMultiByte(CP_ACP, 0, prompt, -1, szDiskPrompt,
174 *pcchDiskPrompt + 1, NULL, NULL);
175
176 done:
177 msi_free(product);
178 msi_free(usersid);
179 msi_free(volume);
180 msi_free(prompt);
181
182 return r;
183 }
184
185 /******************************************************************
186 * MsiSourceListEnumMediaDisksW (MSI.@)
187 */
188 UINT WINAPI MsiSourceListEnumMediaDisksW(LPCWSTR szProductCodeOrPatchCode,
189 LPCWSTR szUserSid, MSIINSTALLCONTEXT dwContext,
190 DWORD dwOptions, DWORD dwIndex, LPDWORD pdwDiskId,
191 LPWSTR szVolumeLabel, LPDWORD pcchVolumeLabel,
192 LPWSTR szDiskPrompt, LPDWORD pcchDiskPrompt)
193 {
194 WCHAR squished_pc[GUID_SIZE];
195 WCHAR convert[11];
196 LPWSTR value = NULL;
197 LPWSTR data = NULL;
198 LPWSTR ptr, ptr2;
199 HKEY source, media;
200 DWORD valuesz, datasz = 0;
201 DWORD type;
202 DWORD numvals, size;
203 LONG res;
204 UINT r;
205 static DWORD index = 0;
206
207 static const WCHAR fmt[] = {'#','%','d',0};
208
209 TRACE("(%s, %s, %d, %d, %d, %p, %p, %p, %p)\n", debugstr_w(szProductCodeOrPatchCode),
210 debugstr_w(szUserSid), dwContext, dwOptions, dwIndex, szVolumeLabel,
211 pcchVolumeLabel, szDiskPrompt, pcchDiskPrompt);
212
213 if (!szProductCodeOrPatchCode ||
214 !squash_guid(szProductCodeOrPatchCode, squished_pc))
215 return ERROR_INVALID_PARAMETER;
216
217 if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid)
218 return ERROR_INVALID_PARAMETER;
219
220 if (dwOptions != MSICODE_PRODUCT && dwOptions != MSICODE_PATCH)
221 return ERROR_INVALID_PARAMETER;
222
223 if (szDiskPrompt && !pcchDiskPrompt)
224 return ERROR_INVALID_PARAMETER;
225
226 if (dwIndex == 0)
227 index = 0;
228
229 if (dwIndex != index)
230 return ERROR_INVALID_PARAMETER;
231
232 r = OpenSourceKey(szProductCodeOrPatchCode, &source,
233 dwOptions, dwContext, FALSE);
234 if (r != ERROR_SUCCESS)
235 return r;
236
237 r = OpenMediaSubkey(source, &media, FALSE);
238 if (r != ERROR_SUCCESS)
239 {
240 RegCloseKey(source);
241 return ERROR_NO_MORE_ITEMS;
242 }
243
244 if (!pcchVolumeLabel && !pcchDiskPrompt)
245 {
246 r = RegEnumValueW(media, dwIndex, NULL, NULL, NULL,
247 &type, NULL, NULL);
248 goto done;
249 }
250
251 res = RegQueryInfoKeyW(media, NULL, NULL, NULL, NULL, NULL,
252 NULL, &numvals, &valuesz, &datasz, NULL, NULL);
253 if (res != ERROR_SUCCESS)
254 {
255 r = ERROR_BAD_CONFIGURATION;
256 goto done;
257 }
258
259 value = msi_alloc(++valuesz * sizeof(WCHAR));
260 data = msi_alloc(++datasz * sizeof(WCHAR));
261 if (!value || !data)
262 {
263 r = ERROR_OUTOFMEMORY;
264 goto done;
265 }
266
267 r = RegEnumValueW(media, dwIndex, value, &valuesz,
268 NULL, &type, (LPBYTE)data, &datasz);
269 if (r != ERROR_SUCCESS)
270 goto done;
271
272 if (pdwDiskId)
273 *pdwDiskId = atolW(value);
274
275 ptr2 = data;
276 ptr = strchrW(data, ';');
277 if (!ptr)
278 ptr = data;
279 else
280 *ptr = '\0';
281
282 if (pcchVolumeLabel)
283 {
284 if (type == REG_DWORD)
285 {
286 sprintfW(convert, fmt, *data);
287 size = lstrlenW(convert);
288 ptr2 = convert;
289 }
290 else
291 size = lstrlenW(data);
292
293 if (size >= *pcchVolumeLabel)
294 r = ERROR_MORE_DATA;
295 else if (szVolumeLabel)
296 lstrcpyW(szVolumeLabel, ptr2);
297
298 *pcchVolumeLabel = size;
299 }
300
301 if (pcchDiskPrompt)
302 {
303 if (!*ptr)
304 ptr++;
305
306 if (type == REG_DWORD)
307 {
308 sprintfW(convert, fmt, *ptr);
309 size = lstrlenW(convert);
310 ptr = convert;
311 }
312 else
313 size = lstrlenW(ptr);
314
315 if (size >= *pcchDiskPrompt)
316 r = ERROR_MORE_DATA;
317 else if (szDiskPrompt)
318 lstrcpyW(szDiskPrompt, ptr);
319
320 *pcchDiskPrompt = size;
321 }
322
323 index++;
324
325 done:
326 msi_free(value);
327 msi_free(data);
328 RegCloseKey(source);
329
330 return r;
331 }
332
333 /******************************************************************
334 * MsiSourceListEnumSourcesA (MSI.@)
335 */
336 UINT WINAPI MsiSourceListEnumSourcesA(LPCSTR szProductCodeOrPatch, LPCSTR szUserSid,
337 MSIINSTALLCONTEXT dwContext,
338 DWORD dwOptions, DWORD dwIndex,
339 LPSTR szSource, LPDWORD pcchSource)
340 {
341 LPWSTR product = NULL;
342 LPWSTR usersid = NULL;
343 LPWSTR source = NULL;
344 DWORD len = 0;
345 UINT r = ERROR_INVALID_PARAMETER;
346 static DWORD index = 0;
347
348 TRACE("(%s, %s, %d, %d, %d, %p, %p)\n", debugstr_a(szProductCodeOrPatch),
349 debugstr_a(szUserSid), dwContext, dwOptions, dwIndex, szSource, pcchSource);
350
351 if (dwIndex == 0)
352 index = 0;
353
354 if (szSource && !pcchSource)
355 goto done;
356
357 if (dwIndex != index)
358 goto done;
359
360 if (szProductCodeOrPatch) product = strdupAtoW(szProductCodeOrPatch);
361 if (szUserSid) usersid = strdupAtoW(szUserSid);
362
363 r = MsiSourceListEnumSourcesW(product, usersid, dwContext, dwOptions,
364 dwIndex, NULL, &len);
365 if (r != ERROR_SUCCESS)
366 goto done;
367
368 source = msi_alloc(++len * sizeof(WCHAR));
369 if (!source)
370 {
371 r = ERROR_OUTOFMEMORY;
372 goto done;
373 }
374
375 *source = '\0';
376 r = MsiSourceListEnumSourcesW(product, usersid, dwContext, dwOptions,
377 dwIndex, source, &len);
378 if (r != ERROR_SUCCESS)
379 goto done;
380
381 len = WideCharToMultiByte(CP_ACP, 0, source, -1, NULL, 0, NULL, NULL);
382 if (pcchSource && *pcchSource >= len)
383 WideCharToMultiByte(CP_ACP, 0, source, -1, szSource, len, NULL, NULL);
384 else if (szSource)
385 r = ERROR_MORE_DATA;
386
387 if (pcchSource)
388 *pcchSource = len - 1;
389
390 done:
391 msi_free(product);
392 msi_free(usersid);
393 msi_free(source);
394
395 if (r == ERROR_SUCCESS)
396 {
397 if (szSource || !pcchSource) index++;
398 }
399 else if (dwIndex > index)
400 index = 0;
401
402 return r;
403 }
404
405 /******************************************************************
406 * MsiSourceListEnumSourcesW (MSI.@)
407 */
408 UINT WINAPI MsiSourceListEnumSourcesW(LPCWSTR szProductCodeOrPatch, LPCWSTR szUserSid,
409 MSIINSTALLCONTEXT dwContext,
410 DWORD dwOptions, DWORD dwIndex,
411 LPWSTR szSource, LPDWORD pcchSource)
412 {
413 WCHAR squished_pc[GUID_SIZE];
414 WCHAR name[32];
415 HKEY source = NULL;
416 HKEY subkey = NULL;
417 LONG res;
418 UINT r = ERROR_INVALID_PARAMETER;
419 static DWORD index = 0;
420
421 static const WCHAR format[] = {'%','d',0};
422
423 TRACE("(%s, %s, %d, %d, %d, %p, %p)\n", debugstr_w(szProductCodeOrPatch),
424 debugstr_w(szUserSid), dwContext, dwOptions, dwIndex, szSource, pcchSource);
425
426 if (dwIndex == 0)
427 index = 0;
428
429 if (!szProductCodeOrPatch || !squash_guid(szProductCodeOrPatch, squished_pc))
430 goto done;
431
432 if (szSource && !pcchSource)
433 goto done;
434
435 if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
436 goto done;
437
438 if ((dwOptions & MSISOURCETYPE_NETWORK) && (dwOptions & MSISOURCETYPE_URL))
439 goto done;
440
441 if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid)
442 goto done;
443
444 if (dwIndex != index)
445 goto done;
446
447 r = OpenSourceKey(szProductCodeOrPatch, &source,
448 dwOptions, dwContext, FALSE);
449 if (r != ERROR_SUCCESS)
450 goto done;
451
452 if (dwOptions & MSISOURCETYPE_NETWORK)
453 r = OpenNetworkSubkey(source, &subkey, FALSE);
454 else if (dwOptions & MSISOURCETYPE_URL)
455 r = OpenURLSubkey(source, &subkey, FALSE);
456
457 if (r != ERROR_SUCCESS)
458 {
459 r = ERROR_NO_MORE_ITEMS;
460 goto done;
461 }
462
463 sprintfW(name, format, dwIndex + 1);
464
465 res = RegQueryValueExW(subkey, name, 0, 0, (LPBYTE)szSource, pcchSource);
466 if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA)
467 r = ERROR_NO_MORE_ITEMS;
468
469 done:
470 RegCloseKey(subkey);
471 RegCloseKey(source);
472
473 if (r == ERROR_SUCCESS)
474 {
475 if (szSource || !pcchSource) index++;
476 }
477 else if (dwIndex > index)
478 index = 0;
479
480 return r;
481 }
482
483 /******************************************************************
484 * MsiSourceListGetInfoA (MSI.@)
485 */
486 UINT WINAPI MsiSourceListGetInfoA( LPCSTR szProduct, LPCSTR szUserSid,
487 MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
488 LPCSTR szProperty, LPSTR szValue,
489 LPDWORD pcchValue)
490 {
491 UINT ret;
492 LPWSTR product = NULL;
493 LPWSTR usersid = NULL;
494 LPWSTR property = NULL;
495 LPWSTR value = NULL;
496 DWORD len = 0;
497
498 if (szValue && !pcchValue)
499 return ERROR_INVALID_PARAMETER;
500
501 if (szProduct) product = strdupAtoW(szProduct);
502 if (szUserSid) usersid = strdupAtoW(szUserSid);
503 if (szProperty) property = strdupAtoW(szProperty);
504
505 ret = MsiSourceListGetInfoW(product, usersid, dwContext, dwOptions,
506 property, NULL, &len);
507 if (ret != ERROR_SUCCESS)
508 goto done;
509
510 value = msi_alloc(++len * sizeof(WCHAR));
511 if (!value)
512 return ERROR_OUTOFMEMORY;
513
514 *value = '\0';
515 ret = MsiSourceListGetInfoW(product, usersid, dwContext, dwOptions,
516 property, value, &len);
517 if (ret != ERROR_SUCCESS)
518 goto done;
519
520 len = WideCharToMultiByte(CP_ACP, 0, value, -1, NULL, 0, NULL, NULL);
521 if (*pcchValue >= len)
522 WideCharToMultiByte(CP_ACP, 0, value, -1, szValue, len, NULL, NULL);
523 else if (szValue)
524 ret = ERROR_MORE_DATA;
525
526 *pcchValue = len - 1;
527
528 done:
529 msi_free(product);
530 msi_free(usersid);
531 msi_free(property);
532 msi_free(value);
533 return ret;
534 }
535
536 /******************************************************************
537 * MsiSourceListGetInfoW (MSI.@)
538 */
539 UINT WINAPI MsiSourceListGetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
540 MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
541 LPCWSTR szProperty, LPWSTR szValue,
542 LPDWORD pcchValue)
543 {
544 WCHAR squished_pc[GUID_SIZE];
545 HKEY sourcekey, media;
546 LPWSTR source, ptr;
547 DWORD size;
548 UINT rc;
549
550 static const WCHAR mediapack[] = {
551 'M','e','d','i','a','P','a','c','k','a','g','e',0};
552
553 TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szProperty));
554
555 if (!szProduct || !squash_guid(szProduct, squished_pc))
556 return ERROR_INVALID_PARAMETER;
557
558 if (szValue && !pcchValue)
559 return ERROR_INVALID_PARAMETER;
560
561 if (dwContext != MSIINSTALLCONTEXT_USERMANAGED &&
562 dwContext != MSIINSTALLCONTEXT_USERUNMANAGED &&
563 dwContext != MSIINSTALLCONTEXT_MACHINE)
564 return ERROR_INVALID_PARAMETER;
565
566 if (!szProperty)
567 return ERROR_INVALID_PARAMETER;
568
569 if (szUserSid)
570 FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
571
572 rc = OpenSourceKey(szProduct, &sourcekey, dwOptions, dwContext, FALSE);
573 if (rc != ERROR_SUCCESS)
574 return rc;
575
576 if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW ) ||
577 !strcmpW( szProperty, INSTALLPROPERTY_DISKPROMPTW ))
578 {
579 rc = OpenMediaSubkey(sourcekey, &media, FALSE);
580 if (rc != ERROR_SUCCESS)
581 {
582 RegCloseKey(sourcekey);
583 return ERROR_SUCCESS;
584 }
585
586 if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW ))
587 szProperty = mediapack;
588
589 RegQueryValueExW(media, szProperty, 0, 0, (LPBYTE)szValue, pcchValue);
590 RegCloseKey(media);
591 }
592 else if (!strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW ) ||
593 !strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDTYPEW ))
594 {
595 rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
596 0, 0, NULL, &size);
597 if (rc != ERROR_SUCCESS)
598 {
599 RegCloseKey(sourcekey);
600 return ERROR_SUCCESS;
601 }
602
603 source = msi_alloc(size);
604 RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
605 0, 0, (LPBYTE)source, &size);
606
607 if (!*source)
608 {
609 msi_free(source);
610 RegCloseKey(sourcekey);
611 return ERROR_SUCCESS;
612 }
613
614 if (!strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDTYPEW ))
615 {
616 if (*source != 'n' && *source != 'u' && *source != 'm')
617 {
618 msi_free(source);
619 RegCloseKey(sourcekey);
620 return ERROR_SUCCESS;
621 }
622
623 ptr = source;
624 source[1] = '\0';
625 }
626 else
627 {
628 ptr = strrchrW(source, ';');
629 if (!ptr)
630 ptr = source;
631 else
632 ptr++;
633 }
634
635 if (szValue)
636 {
637 if (strlenW(ptr) < *pcchValue)
638 lstrcpyW(szValue, ptr);
639 else
640 rc = ERROR_MORE_DATA;
641 }
642
643 *pcchValue = lstrlenW(ptr);
644 msi_free(source);
645 }
646 else if (!strcmpW( szProperty, INSTALLPROPERTY_PACKAGENAMEW ))
647 {
648 *pcchValue = *pcchValue * sizeof(WCHAR);
649 rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, 0,
650 (LPBYTE)szValue, pcchValue);
651 if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
652 {
653 *pcchValue = 0;
654 rc = ERROR_SUCCESS;
655 }
656 else
657 {
658 if (*pcchValue)
659 *pcchValue = (*pcchValue - 1) / sizeof(WCHAR);
660 if (szValue)
661 szValue[*pcchValue] = '\0';
662 }
663 }
664 else
665 {
666 FIXME("Unknown property %s\n",debugstr_w(szProperty));
667 rc = ERROR_UNKNOWN_PROPERTY;
668 }
669
670 RegCloseKey(sourcekey);
671 return rc;
672 }
673
674 /******************************************************************
675 * MsiSourceListSetInfoA (MSI.@)
676 */
677 UINT WINAPI MsiSourceListSetInfoA(LPCSTR szProduct, LPCSTR szUserSid,
678 MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
679 LPCSTR szProperty, LPCSTR szValue)
680 {
681 UINT ret;
682 LPWSTR product = NULL;
683 LPWSTR usersid = NULL;
684 LPWSTR property = NULL;
685 LPWSTR value = NULL;
686
687 if (szProduct) product = strdupAtoW(szProduct);
688 if (szUserSid) usersid = strdupAtoW(szUserSid);
689 if (szProperty) property = strdupAtoW(szProperty);
690 if (szValue) value = strdupAtoW(szValue);
691
692 ret = MsiSourceListSetInfoW(product, usersid, dwContext, dwOptions,
693 property, value);
694
695 msi_free(product);
696 msi_free(usersid);
697 msi_free(property);
698 msi_free(value);
699
700 return ret;
701 }
702
703 UINT msi_set_last_used_source(LPCWSTR product, LPCWSTR usersid,
704 MSIINSTALLCONTEXT context, DWORD options,
705 LPCWSTR value)
706 {
707 HKEY source;
708 LPWSTR buffer;
709 WCHAR typechar;
710 DWORD size;
711 UINT r;
712 int index = 1;
713
714 static const WCHAR format[] = {'%','c',';','%','i',';','%','s',0};
715
716 if (options & MSISOURCETYPE_NETWORK)
717 typechar = 'n';
718 else if (options & MSISOURCETYPE_URL)
719 typechar = 'u';
720 else if (options & MSISOURCETYPE_MEDIA)
721 typechar = 'm';
722 else
723 return ERROR_INVALID_PARAMETER;
724
725 if (!(options & MSISOURCETYPE_MEDIA))
726 {
727 r = MsiSourceListAddSourceExW(product, usersid, context,
728 options, value, 0);
729 if (r != ERROR_SUCCESS)
730 return r;
731
732 index = 0;
733 while ((r = MsiSourceListEnumSourcesW(product, usersid, context, options,
734 index, NULL, NULL)) == ERROR_SUCCESS)
735 index++;
736
737 if (r != ERROR_NO_MORE_ITEMS)
738 return r;
739 }
740
741 size = (lstrlenW(format) + lstrlenW(value) + 7) * sizeof(WCHAR);
742 buffer = msi_alloc(size);
743 if (!buffer)
744 return ERROR_OUTOFMEMORY;
745
746 r = OpenSourceKey(product, &source, MSICODE_PRODUCT, context, FALSE);
747 if (r != ERROR_SUCCESS)
748 {
749 msi_free(buffer);
750 return r;
751 }
752
753 sprintfW(buffer, format, typechar, index, value);
754
755 size = (lstrlenW(buffer) + 1) * sizeof(WCHAR);
756 r = RegSetValueExW(source, INSTALLPROPERTY_LASTUSEDSOURCEW, 0,
757 REG_SZ, (LPBYTE)buffer, size);
758 msi_free(buffer);
759
760 RegCloseKey(source);
761 return r;
762 }
763
764 /******************************************************************
765 * MsiSourceListSetInfoW (MSI.@)
766 */
767 UINT WINAPI MsiSourceListSetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
768 MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
769 LPCWSTR szProperty, LPCWSTR szValue)
770 {
771 WCHAR squished_pc[GUID_SIZE];
772 HKEY sourcekey, media;
773 LPCWSTR property;
774 UINT rc;
775
776 static const WCHAR media_package[] = {
777 'M','e','d','i','a','P','a','c','k','a','g','e',0
778 };
779
780 TRACE("%s %s %x %x %s %s\n", debugstr_w(szProduct), debugstr_w(szUserSid),
781 dwContext, dwOptions, debugstr_w(szProperty), debugstr_w(szValue));
782
783 if (!szProduct || !squash_guid(szProduct, squished_pc))
784 return ERROR_INVALID_PARAMETER;
785
786 if (!szProperty)
787 return ERROR_INVALID_PARAMETER;
788
789 if (!szValue)
790 return ERROR_UNKNOWN_PROPERTY;
791
792 if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid)
793 return ERROR_INVALID_PARAMETER;
794
795 if (dwOptions & MSICODE_PATCH)
796 {
797 FIXME("Unhandled options MSICODE_PATCH\n");
798 return ERROR_UNKNOWN_PATCH;
799 }
800
801 property = szProperty;
802 if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW ))
803 property = media_package;
804
805 rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
806 if (rc != ERROR_SUCCESS)
807 return rc;
808
809 if (strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW ) &&
810 dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL))
811 {
812 RegCloseKey(sourcekey);
813 return ERROR_INVALID_PARAMETER;
814 }
815
816 if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW ) ||
817 !strcmpW( szProperty, INSTALLPROPERTY_DISKPROMPTW ))
818 {
819 rc = OpenMediaSubkey(sourcekey, &media, TRUE);
820 if (rc == ERROR_SUCCESS)
821 {
822 rc = msi_reg_set_val_str(media, property, szValue);
823 RegCloseKey(media);
824 }
825 }
826 else if (!strcmpW( szProperty, INSTALLPROPERTY_PACKAGENAMEW ))
827 {
828 DWORD size = (lstrlenW(szValue) + 1) * sizeof(WCHAR);
829 rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0,
830 REG_SZ, (const BYTE *)szValue, size);
831 if (rc != ERROR_SUCCESS)
832 rc = ERROR_UNKNOWN_PROPERTY;
833 }
834 else if (!strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW ))
835 {
836 if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
837 rc = ERROR_INVALID_PARAMETER;
838 else
839 rc = msi_set_last_used_source(szProduct, szUserSid, dwContext,
840 dwOptions, szValue);
841 }
842 else
843 rc = ERROR_UNKNOWN_PROPERTY;
844
845 RegCloseKey(sourcekey);
846 return rc;
847 }
848
849 /******************************************************************
850 * MsiSourceListAddSourceW (MSI.@)
851 */
852 UINT WINAPI MsiSourceListAddSourceW( LPCWSTR szProduct, LPCWSTR szUserName,
853 DWORD dwReserved, LPCWSTR szSource)
854 {
855 WCHAR squished_pc[GUID_SIZE];
856 INT ret;
857 LPWSTR sidstr = NULL;
858 DWORD sidsize = 0;
859 DWORD domsize = 0;
860 DWORD context;
861 HKEY hkey = 0;
862 UINT r;
863
864 TRACE("%s %s %s\n", debugstr_w(szProduct), debugstr_w(szUserName), debugstr_w(szSource));
865
866 if (!szSource || !*szSource)
867 return ERROR_INVALID_PARAMETER;
868
869 if (dwReserved != 0)
870 return ERROR_INVALID_PARAMETER;
871
872 if (!szProduct || !squash_guid(szProduct, squished_pc))
873 return ERROR_INVALID_PARAMETER;
874
875 if (!szUserName || !*szUserName)
876 context = MSIINSTALLCONTEXT_MACHINE;
877 else
878 {
879 if (LookupAccountNameW(NULL, szUserName, NULL, &sidsize, NULL, &domsize, NULL))
880 {
881 PSID psid = msi_alloc(sidsize);
882
883 if (LookupAccountNameW(NULL, szUserName, psid, &sidsize, NULL, &domsize, NULL))
884 ConvertSidToStringSidW(psid, &sidstr);
885
886 msi_free(psid);
887 }
888
889 r = MSIREG_OpenProductKey(szProduct, NULL,
890 MSIINSTALLCONTEXT_USERMANAGED, &hkey, FALSE);
891 if (r == ERROR_SUCCESS)
892 context = MSIINSTALLCONTEXT_USERMANAGED;
893 else
894 {
895 r = MSIREG_OpenProductKey(szProduct, NULL,
896 MSIINSTALLCONTEXT_USERUNMANAGED,
897 &hkey, FALSE);
898 if (r != ERROR_SUCCESS)
899 return ERROR_UNKNOWN_PRODUCT;
900
901 context = MSIINSTALLCONTEXT_USERUNMANAGED;
902 }
903
904 RegCloseKey(hkey);
905 }
906
907 ret = MsiSourceListAddSourceExW(szProduct, sidstr,
908 context, MSISOURCETYPE_NETWORK, szSource, 0);
909
910 if (sidstr)
911 LocalFree(sidstr);
912
913 return ret;
914 }
915
916 /******************************************************************
917 * MsiSourceListAddSourceA (MSI.@)
918 */
919 UINT WINAPI MsiSourceListAddSourceA( LPCSTR szProduct, LPCSTR szUserName,
920 DWORD dwReserved, LPCSTR szSource)
921 {
922 INT ret;
923 LPWSTR szwproduct;
924 LPWSTR szwusername;
925 LPWSTR szwsource;
926
927 szwproduct = strdupAtoW( szProduct );
928 szwusername = strdupAtoW( szUserName );
929 szwsource = strdupAtoW( szSource );
930
931 ret = MsiSourceListAddSourceW(szwproduct, szwusername, dwReserved, szwsource);
932
933 msi_free(szwproduct);
934 msi_free(szwusername);
935 msi_free(szwsource);
936
937 return ret;
938 }
939
940 /******************************************************************
941 * MsiSourceListAddSourceExA (MSI.@)
942 */
943 UINT WINAPI MsiSourceListAddSourceExA(LPCSTR szProduct, LPCSTR szUserSid,
944 MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCSTR szSource, DWORD dwIndex)
945 {
946 UINT ret;
947 LPWSTR product, usersid, source;
948
949 product = strdupAtoW(szProduct);
950 usersid = strdupAtoW(szUserSid);
951 source = strdupAtoW(szSource);
952
953 ret = MsiSourceListAddSourceExW(product, usersid, dwContext,
954 dwOptions, source, dwIndex);
955
956 msi_free(product);
957 msi_free(usersid);
958 msi_free(source);
959
960 return ret;
961 }
962
963 static void free_source_list(struct list *sourcelist)
964 {
965 while (!list_empty(sourcelist))
966 {
967 media_info *info = LIST_ENTRY(list_head(sourcelist), media_info, entry);
968 list_remove(&info->entry);
969 msi_free(info->path);
970 msi_free(info);
971 }
972 }
973
974 static void add_source_to_list(struct list *sourcelist, media_info *info,
975 DWORD *index)
976 {
977 media_info *iter;
978 BOOL found = FALSE;
979 static const WCHAR fmt[] = {'%','i',0};
980
981 if (index) *index = 0;
982
983 if (list_empty(sourcelist))
984 {
985 list_add_head(sourcelist, &info->entry);
986 return;
987 }
988
989 LIST_FOR_EACH_ENTRY(iter, sourcelist, media_info, entry)
990 {
991 if (!found && info->index < iter->index)
992 {
993 found = TRUE;
994 list_add_before(&iter->entry, &info->entry);
995 }
996
997 /* update the rest of the list */
998 if (found)
999 sprintfW(iter->szIndex, fmt, ++iter->index);
1000 else if (index)
1001 (*index)++;
1002 }
1003
1004 if (!found)
1005 list_add_after(&iter->entry, &info->entry);
1006 }
1007
1008 static UINT fill_source_list(struct list *sourcelist, HKEY sourcekey, DWORD *count)
1009 {
1010 UINT r = ERROR_SUCCESS;
1011 DWORD index = 0;
1012 WCHAR name[10];
1013 DWORD size, val_size;
1014 media_info *entry;
1015
1016 *count = 0;
1017
1018 while (r == ERROR_SUCCESS)
1019 {
1020 size = sizeof(name) / sizeof(name[0]);
1021 r = RegEnumValueW(sourcekey, index, name, &size, NULL, NULL, NULL, &val_size);
1022 if (r != ERROR_SUCCESS)
1023 return r;
1024
1025 entry = msi_alloc(sizeof(media_info));
1026 if (!entry)
1027 goto error;
1028
1029 entry->path = msi_alloc(val_size);
1030 if (!entry->path)
1031 {
1032 msi_free(entry);
1033 goto error;
1034 }
1035
1036 lstrcpyW(entry->szIndex, name);
1037 entry->index = atoiW(name);
1038
1039 size++;
1040 r = RegEnumValueW(sourcekey, index, name, &size, NULL,
1041 NULL, (LPBYTE)entry->path, &val_size);
1042 if (r != ERROR_SUCCESS)
1043 {
1044 msi_free(entry->path);
1045 msi_free(entry);
1046 goto error;
1047 }
1048
1049 index = ++(*count);
1050 add_source_to_list(sourcelist, entry, NULL);
1051 }
1052
1053 error:
1054 *count = -1;
1055 free_source_list(sourcelist);
1056 return ERROR_OUTOFMEMORY;
1057 }
1058
1059 /******************************************************************
1060 * MsiSourceListAddSourceExW (MSI.@)
1061 */
1062 UINT WINAPI MsiSourceListAddSourceExW( LPCWSTR szProduct, LPCWSTR szUserSid,
1063 MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szSource,
1064 DWORD dwIndex)
1065 {
1066 HKEY sourcekey;
1067 HKEY typekey;
1068 UINT rc;
1069 struct list sourcelist;
1070 media_info *info;
1071 WCHAR squished_pc[GUID_SIZE];
1072 WCHAR name[10];
1073 LPWSTR source;
1074 LPCWSTR postfix;
1075 DWORD size, count;
1076 DWORD index;
1077
1078 static const WCHAR fmt[] = {'%','i',0};
1079
1080 TRACE("%s %s %x %x %s %i\n", debugstr_w(szProduct), debugstr_w(szUserSid),
1081 dwContext, dwOptions, debugstr_w(szSource), dwIndex);
1082
1083 if (!szProduct || !squash_guid(szProduct, squished_pc))
1084 return ERROR_INVALID_PARAMETER;
1085
1086 if (!szSource || !*szSource)
1087 return ERROR_INVALID_PARAMETER;
1088
1089 if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
1090 return ERROR_INVALID_PARAMETER;
1091
1092 if (dwOptions & MSICODE_PATCH)
1093 {
1094 FIXME("Unhandled options MSICODE_PATCH\n");
1095 return ERROR_FUNCTION_FAILED;
1096 }
1097
1098 if (szUserSid && (dwContext & MSIINSTALLCONTEXT_MACHINE))
1099 return ERROR_INVALID_PARAMETER;
1100
1101 rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
1102 if (rc != ERROR_SUCCESS)
1103 return rc;
1104
1105 if (dwOptions & MSISOURCETYPE_NETWORK)
1106 rc = OpenNetworkSubkey(sourcekey, &typekey, TRUE);
1107 else if (dwOptions & MSISOURCETYPE_URL)
1108 rc = OpenURLSubkey(sourcekey, &typekey, TRUE);
1109 else if (dwOptions & MSISOURCETYPE_MEDIA)
1110 rc = OpenMediaSubkey(sourcekey, &typekey, TRUE);
1111 else
1112 {
1113 ERR("unknown media type: %08x\n", dwOptions);
1114 RegCloseKey(sourcekey);
1115 return ERROR_FUNCTION_FAILED;
1116 }
1117 if (rc != ERROR_SUCCESS)
1118 {
1119 ERR("can't open subkey %u\n", rc);
1120 RegCloseKey(sourcekey);
1121 return rc;
1122 }
1123
1124 postfix = (dwOptions & MSISOURCETYPE_NETWORK) ? szBackSlash : szForwardSlash;
1125 if (szSource[lstrlenW(szSource) - 1] == *postfix)
1126 source = strdupW(szSource);
1127 else
1128 {
1129 size = lstrlenW(szSource) + 2;
1130 source = msi_alloc(size * sizeof(WCHAR));
1131 lstrcpyW(source, szSource);
1132 lstrcatW(source, postfix);
1133 }
1134
1135 list_init(&sourcelist);
1136 rc = fill_source_list(&sourcelist, typekey, &count);
1137 if (rc != ERROR_NO_MORE_ITEMS)
1138 goto done;
1139
1140 size = (lstrlenW(source) + 1) * sizeof(WCHAR);
1141
1142 if (count == 0)
1143 {
1144 rc = RegSetValueExW(typekey, szOne, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
1145 goto done;
1146 }
1147 else if (dwIndex > count || dwIndex == 0)
1148 {
1149 sprintfW(name, fmt, count + 1);
1150 rc = RegSetValueExW(typekey, name, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
1151 goto done;
1152 }
1153 else
1154 {
1155 sprintfW(name, fmt, dwIndex);
1156 info = msi_alloc(sizeof(media_info));
1157 if (!info)
1158 {
1159 rc = ERROR_OUTOFMEMORY;
1160 goto done;
1161 }
1162
1163 info->path = strdupW(source);
1164 lstrcpyW(info->szIndex, name);
1165 info->index = dwIndex;
1166 add_source_to_list(&sourcelist, info, &index);
1167
1168 LIST_FOR_EACH_ENTRY(info, &sourcelist, media_info, entry)
1169 {
1170 if (info->index < index)
1171 continue;
1172
1173 size = (lstrlenW(info->path) + 1) * sizeof(WCHAR);
1174 rc = RegSetValueExW(typekey, info->szIndex, 0,
1175 REG_EXPAND_SZ, (LPBYTE)info->path, size);
1176 if (rc != ERROR_SUCCESS)
1177 goto done;
1178 }
1179 }
1180
1181 done:
1182 free_source_list(&sourcelist);
1183 msi_free(source);
1184 RegCloseKey(typekey);
1185 RegCloseKey(sourcekey);
1186 return rc;
1187 }
1188
1189 /******************************************************************
1190 * MsiSourceListAddMediaDiskA (MSI.@)
1191 */
1192 UINT WINAPI MsiSourceListAddMediaDiskA(LPCSTR szProduct, LPCSTR szUserSid,
1193 MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId,
1194 LPCSTR szVolumeLabel, LPCSTR szDiskPrompt)
1195 {
1196 UINT r;
1197 LPWSTR product = NULL;
1198 LPWSTR usersid = NULL;
1199 LPWSTR volume = NULL;
1200 LPWSTR prompt = NULL;
1201
1202 if (szProduct) product = strdupAtoW(szProduct);
1203 if (szUserSid) usersid = strdupAtoW(szUserSid);
1204 if (szVolumeLabel) volume = strdupAtoW(szVolumeLabel);
1205 if (szDiskPrompt) prompt = strdupAtoW(szDiskPrompt);
1206
1207 r = MsiSourceListAddMediaDiskW(product, usersid, dwContext, dwOptions,
1208 dwDiskId, volume, prompt);
1209
1210 msi_free(product);
1211 msi_free(usersid);
1212 msi_free(volume);
1213 msi_free(prompt);
1214
1215 return r;
1216 }
1217
1218 /******************************************************************
1219 * MsiSourceListAddMediaDiskW (MSI.@)
1220 */
1221 UINT WINAPI MsiSourceListAddMediaDiskW(LPCWSTR szProduct, LPCWSTR szUserSid,
1222 MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId,
1223 LPCWSTR szVolumeLabel, LPCWSTR szDiskPrompt)
1224 {
1225 HKEY sourcekey;
1226 HKEY mediakey;
1227 UINT rc;
1228 WCHAR szIndex[10];
1229 WCHAR squished_pc[GUID_SIZE];
1230 LPWSTR buffer;
1231 DWORD size;
1232
1233 static const WCHAR fmt[] = {'%','i',0};
1234
1235 TRACE("%s %s %x %x %i %s %s\n", debugstr_w(szProduct),
1236 debugstr_w(szUserSid), dwContext, dwOptions, dwDiskId,
1237 debugstr_w(szVolumeLabel), debugstr_w(szDiskPrompt));
1238
1239 if (!szProduct || !squash_guid(szProduct, squished_pc))
1240 return ERROR_INVALID_PARAMETER;
1241
1242 if (dwOptions != MSICODE_PRODUCT && dwOptions != MSICODE_PATCH)
1243 return ERROR_INVALID_PARAMETER;
1244
1245 if ((szVolumeLabel && !*szVolumeLabel) || (szDiskPrompt && !*szDiskPrompt))
1246 return ERROR_INVALID_PARAMETER;
1247
1248 if ((dwContext & MSIINSTALLCONTEXT_MACHINE) && szUserSid)
1249 return ERROR_INVALID_PARAMETER;
1250
1251 if (dwOptions & MSICODE_PATCH)
1252 {
1253 FIXME("Unhandled options MSICODE_PATCH\n");
1254 return ERROR_FUNCTION_FAILED;
1255 }
1256
1257 rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
1258 if (rc != ERROR_SUCCESS)
1259 return rc;
1260
1261 OpenMediaSubkey(sourcekey, &mediakey, TRUE);
1262
1263 sprintfW(szIndex, fmt, dwDiskId);
1264
1265 size = 2;
1266 if (szVolumeLabel) size += lstrlenW(szVolumeLabel);
1267 if (szDiskPrompt) size += lstrlenW(szDiskPrompt);
1268
1269 size *= sizeof(WCHAR);
1270 buffer = msi_alloc(size);
1271 *buffer = '\0';
1272
1273 if (szVolumeLabel) lstrcpyW(buffer, szVolumeLabel);
1274 lstrcatW(buffer, szSemiColon);
1275 if (szDiskPrompt) lstrcatW(buffer, szDiskPrompt);
1276
1277 RegSetValueExW(mediakey, szIndex, 0, REG_SZ, (LPBYTE)buffer, size);
1278 msi_free(buffer);
1279
1280 RegCloseKey(sourcekey);
1281 RegCloseKey(mediakey);
1282
1283 return ERROR_SUCCESS;
1284 }
1285
1286 /******************************************************************
1287 * MsiSourceListClearAllA (MSI.@)
1288 */
1289 UINT WINAPI MsiSourceListClearAllA( LPCSTR szProduct, LPCSTR szUserName, DWORD dwReserved )
1290 {
1291 FIXME("(%s %s %d)\n", debugstr_a(szProduct), debugstr_a(szUserName), dwReserved);
1292 return ERROR_SUCCESS;
1293 }
1294
1295 /******************************************************************
1296 * MsiSourceListClearAllW (MSI.@)
1297 */
1298 UINT WINAPI MsiSourceListClearAllW( LPCWSTR szProduct, LPCWSTR szUserName, DWORD dwReserved )
1299 {
1300 FIXME("(%s %s %d)\n", debugstr_w(szProduct), debugstr_w(szUserName), dwReserved);
1301 return ERROR_SUCCESS;
1302 }
1303
1304 /******************************************************************
1305 * MsiSourceListClearAllExA (MSI.@)
1306 */
1307 UINT WINAPI MsiSourceListClearAllExA( LPCSTR szProduct, LPCSTR szUserSid,
1308 MSIINSTALLCONTEXT dwContext, DWORD dwOptions )
1309 {
1310 FIXME("(%s %s %d %08x)\n", debugstr_a(szProduct), debugstr_a(szUserSid),
1311 dwContext, dwOptions);
1312 return ERROR_SUCCESS;
1313 }
1314
1315 /******************************************************************
1316 * MsiSourceListClearAllExW (MSI.@)
1317 */
1318 UINT WINAPI MsiSourceListClearAllExW( LPCWSTR szProduct, LPCWSTR szUserSid,
1319 MSIINSTALLCONTEXT dwContext, DWORD dwOptions )
1320 {
1321 FIXME("(%s %s %d %08x)\n", debugstr_w(szProduct), debugstr_w(szUserSid),
1322 dwContext, dwOptions);
1323 return ERROR_SUCCESS;
1324 }
1325
1326 /******************************************************************
1327 * MsiSourceListClearSourceA (MSI.@)
1328 */
1329 UINT WINAPI MsiSourceListClearSourceA(LPCSTR szProductCodeOrPatchCode, LPCSTR szUserSid,
1330 MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
1331 LPCSTR szSource)
1332 {
1333 FIXME("(%s %s %x %x %s)\n", debugstr_a(szProductCodeOrPatchCode), debugstr_a(szUserSid),
1334 dwContext, dwOptions, debugstr_a(szSource));
1335 return ERROR_SUCCESS;
1336 }
1337
1338 /******************************************************************
1339 * MsiSourceListClearSourceW (MSI.@)
1340 */
1341 UINT WINAPI MsiSourceListClearSourceW(LPCWSTR szProductCodeOrPatchCode, LPCWSTR szUserSid,
1342 MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
1343 LPCWSTR szSource)
1344 {
1345 FIXME("(%s %s %x %x %s)\n", debugstr_w(szProductCodeOrPatchCode), debugstr_w(szUserSid),
1346 dwContext, dwOptions, debugstr_w(szSource));
1347 return ERROR_SUCCESS;
1348 }