summaryrefslogtreecommitdiffstats
path: root/libmsi/source.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmsi/source.c')
-rw-r--r--libmsi/source.c1363
1 files changed, 1363 insertions, 0 deletions
diff --git a/libmsi/source.c b/libmsi/source.c
new file mode 100644
index 0000000..881f3d2
--- /dev/null
+++ b/libmsi/source.c
@@ -0,0 +1,1363 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2005 Aric Stewart for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winnls.h"
+#include "shlwapi.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+#include "wincrypt.h"
+#include "winver.h"
+#include "winuser.h"
+#include "wine/unicode.h"
+#include "sddl.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/*
+ * These apis are defined in MSI 3.0
+ */
+
+typedef struct tagMediaInfo
+{
+ struct list entry;
+ LPWSTR path;
+ WCHAR szIndex[10];
+ DWORD index;
+} media_info;
+
+static UINT OpenSourceKey(LPCWSTR szProduct, HKEY* key, DWORD dwOptions,
+ MSIINSTALLCONTEXT context, BOOL create)
+{
+ HKEY rootkey = 0;
+ UINT rc = ERROR_FUNCTION_FAILED;
+
+ if (context == MSIINSTALLCONTEXT_USERUNMANAGED)
+ {
+ if (dwOptions & MSICODE_PATCH)
+ rc = MSIREG_OpenUserPatchesKey(szProduct, &rootkey, create);
+ else
+ rc = MSIREG_OpenProductKey(szProduct, NULL, context,
+ &rootkey, create);
+ }
+ else if (context == MSIINSTALLCONTEXT_USERMANAGED)
+ {
+ if (dwOptions & MSICODE_PATCH)
+ rc = MSIREG_OpenUserPatchesKey(szProduct, &rootkey, create);
+ else
+ rc = MSIREG_OpenProductKey(szProduct, NULL, context,
+ &rootkey, create);
+ }
+ else if (context == MSIINSTALLCONTEXT_MACHINE)
+ {
+ if (dwOptions & MSICODE_PATCH)
+ rc = MSIREG_OpenPatchesKey(szProduct, &rootkey, create);
+ else
+ rc = MSIREG_OpenProductKey(szProduct, NULL, context,
+ &rootkey, create);
+ }
+
+ if (rc != ERROR_SUCCESS)
+ {
+ if (dwOptions & MSICODE_PATCH)
+ return ERROR_UNKNOWN_PATCH;
+ else
+ return ERROR_UNKNOWN_PRODUCT;
+ }
+
+ if (create)
+ rc = RegCreateKeyW(rootkey, szSourceList, key);
+ else
+ {
+ rc = RegOpenKeyW(rootkey,szSourceList, key);
+ if (rc != ERROR_SUCCESS)
+ rc = ERROR_BAD_CONFIGURATION;
+ }
+
+ return rc;
+}
+
+static UINT OpenMediaSubkey(HKEY rootkey, HKEY *key, BOOL create)
+{
+ UINT rc;
+ static const WCHAR media[] = {'M','e','d','i','a',0};
+
+ if (create)
+ rc = RegCreateKeyW(rootkey, media, key);
+ else
+ rc = RegOpenKeyW(rootkey,media, key);
+
+ return rc;
+}
+
+static UINT OpenNetworkSubkey(HKEY rootkey, HKEY *key, BOOL create)
+{
+ UINT rc;
+ static const WCHAR net[] = {'N','e','t',0};
+
+ if (create)
+ rc = RegCreateKeyW(rootkey, net, key);
+ else
+ rc = RegOpenKeyW(rootkey, net, key);
+
+ return rc;
+}
+
+static UINT OpenURLSubkey(HKEY rootkey, HKEY *key, BOOL create)
+{
+ UINT rc;
+ static const WCHAR URL[] = {'U','R','L',0};
+
+ if (create)
+ rc = RegCreateKeyW(rootkey, URL, key);
+ else
+ rc = RegOpenKeyW(rootkey, URL, key);
+
+ return rc;
+}
+
+/******************************************************************
+ * MsiSourceListEnumMediaDisksA (MSI.@)
+ */
+UINT WINAPI MsiSourceListEnumMediaDisksA(LPCSTR szProductCodeOrPatchCode,
+ LPCSTR szUserSid, MSIINSTALLCONTEXT dwContext,
+ DWORD dwOptions, DWORD dwIndex, LPDWORD pdwDiskId,
+ LPSTR szVolumeLabel, LPDWORD pcchVolumeLabel,
+ LPSTR szDiskPrompt, LPDWORD pcchDiskPrompt)
+{
+ LPWSTR product = NULL;
+ LPWSTR usersid = NULL;
+ LPWSTR volume = NULL;
+ LPWSTR prompt = NULL;
+ UINT r = ERROR_INVALID_PARAMETER;
+
+ TRACE("(%s, %s, %d, %d, %d, %p, %p, %p, %p, %p)\n", debugstr_a(szProductCodeOrPatchCode),
+ debugstr_a(szUserSid), dwContext, dwOptions, dwIndex, pdwDiskId,
+ szVolumeLabel, pcchVolumeLabel, szDiskPrompt, pcchDiskPrompt);
+
+ if (szDiskPrompt && !pcchDiskPrompt)
+ return ERROR_INVALID_PARAMETER;
+
+ if (szProductCodeOrPatchCode) product = strdupAtoW(szProductCodeOrPatchCode);
+ if (szUserSid) usersid = strdupAtoW(szUserSid);
+
+ /* FIXME: add tests for an invalid format */
+
+ if (pcchVolumeLabel)
+ volume = msi_alloc(*pcchVolumeLabel * sizeof(WCHAR));
+
+ if (pcchDiskPrompt)
+ prompt = msi_alloc(*pcchDiskPrompt * sizeof(WCHAR));
+
+ if (volume) *volume = '\0';
+ if (prompt) *prompt = '\0';
+ r = MsiSourceListEnumMediaDisksW(product, usersid, dwContext, dwOptions,
+ dwIndex, pdwDiskId, volume, pcchVolumeLabel,
+ prompt, pcchDiskPrompt);
+ if (r != ERROR_SUCCESS)
+ goto done;
+
+ if (szVolumeLabel && pcchVolumeLabel)
+ WideCharToMultiByte(CP_ACP, 0, volume, -1, szVolumeLabel,
+ *pcchVolumeLabel + 1, NULL, NULL);
+
+ if (szDiskPrompt)
+ WideCharToMultiByte(CP_ACP, 0, prompt, -1, szDiskPrompt,
+ *pcchDiskPrompt + 1, NULL, NULL);
+
+done:
+ msi_free(product);
+ msi_free(usersid);
+ msi_free(volume);
+ msi_free(prompt);
+
+ return r;
+}
+
+/******************************************************************
+ * MsiSourceListEnumMediaDisksW (MSI.@)
+ */
+UINT WINAPI MsiSourceListEnumMediaDisksW(LPCWSTR szProductCodeOrPatchCode,
+ LPCWSTR szUserSid, MSIINSTALLCONTEXT dwContext,
+ DWORD dwOptions, DWORD dwIndex, LPDWORD pdwDiskId,
+ LPWSTR szVolumeLabel, LPDWORD pcchVolumeLabel,
+ LPWSTR szDiskPrompt, LPDWORD pcchDiskPrompt)
+{
+ WCHAR squished_pc[GUID_SIZE];
+ WCHAR convert[11];
+ LPWSTR value = NULL;
+ LPWSTR data = NULL;
+ LPWSTR ptr, ptr2;
+ HKEY source, media;
+ DWORD valuesz, datasz = 0;
+ DWORD type;
+ DWORD numvals, size;
+ LONG res;
+ UINT r;
+ static DWORD index = 0;
+
+ static const WCHAR fmt[] = {'#','%','d',0};
+
+ TRACE("(%s, %s, %d, %d, %d, %p, %p, %p, %p)\n", debugstr_w(szProductCodeOrPatchCode),
+ debugstr_w(szUserSid), dwContext, dwOptions, dwIndex, szVolumeLabel,
+ pcchVolumeLabel, szDiskPrompt, pcchDiskPrompt);
+
+ if (!szProductCodeOrPatchCode ||
+ !squash_guid(szProductCodeOrPatchCode, squished_pc))
+ return ERROR_INVALID_PARAMETER;
+
+ if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid)
+ return ERROR_INVALID_PARAMETER;
+
+ if (dwOptions != MSICODE_PRODUCT && dwOptions != MSICODE_PATCH)
+ return ERROR_INVALID_PARAMETER;
+
+ if (szDiskPrompt && !pcchDiskPrompt)
+ return ERROR_INVALID_PARAMETER;
+
+ if (dwIndex == 0)
+ index = 0;
+
+ if (dwIndex != index)
+ return ERROR_INVALID_PARAMETER;
+
+ r = OpenSourceKey(szProductCodeOrPatchCode, &source,
+ dwOptions, dwContext, FALSE);
+ if (r != ERROR_SUCCESS)
+ return r;
+
+ r = OpenMediaSubkey(source, &media, FALSE);
+ if (r != ERROR_SUCCESS)
+ {
+ RegCloseKey(source);
+ return ERROR_NO_MORE_ITEMS;
+ }
+
+ if (!pcchVolumeLabel && !pcchDiskPrompt)
+ {
+ r = RegEnumValueW(media, dwIndex, NULL, NULL, NULL,
+ &type, NULL, NULL);
+ goto done;
+ }
+
+ res = RegQueryInfoKeyW(media, NULL, NULL, NULL, NULL, NULL,
+ NULL, &numvals, &valuesz, &datasz, NULL, NULL);
+ if (res != ERROR_SUCCESS)
+ {
+ r = ERROR_BAD_CONFIGURATION;
+ goto done;
+ }
+
+ value = msi_alloc(++valuesz * sizeof(WCHAR));
+ data = msi_alloc(++datasz * sizeof(WCHAR));
+ if (!value || !data)
+ {
+ r = ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ r = RegEnumValueW(media, dwIndex, value, &valuesz,
+ NULL, &type, (LPBYTE)data, &datasz);
+ if (r != ERROR_SUCCESS)
+ goto done;
+
+ if (pdwDiskId)
+ *pdwDiskId = atolW(value);
+
+ ptr2 = data;
+ ptr = strchrW(data, ';');
+ if (!ptr)
+ ptr = data;
+ else
+ *ptr = '\0';
+
+ if (pcchVolumeLabel)
+ {
+ if (type == REG_DWORD)
+ {
+ sprintfW(convert, fmt, *data);
+ size = lstrlenW(convert);
+ ptr2 = convert;
+ }
+ else
+ size = lstrlenW(data);
+
+ if (size >= *pcchVolumeLabel)
+ r = ERROR_MORE_DATA;
+ else if (szVolumeLabel)
+ lstrcpyW(szVolumeLabel, ptr2);
+
+ *pcchVolumeLabel = size;
+ }
+
+ if (pcchDiskPrompt)
+ {
+ if (!*ptr)
+ ptr++;
+
+ if (type == REG_DWORD)
+ {
+ sprintfW(convert, fmt, *ptr);
+ size = lstrlenW(convert);
+ ptr = convert;
+ }
+ else
+ size = lstrlenW(ptr);
+
+ if (size >= *pcchDiskPrompt)
+ r = ERROR_MORE_DATA;
+ else if (szDiskPrompt)
+ lstrcpyW(szDiskPrompt, ptr);
+
+ *pcchDiskPrompt = size;
+ }
+
+ index++;
+
+done:
+ msi_free(value);
+ msi_free(data);
+ RegCloseKey(source);
+
+ return r;
+}
+
+/******************************************************************
+ * MsiSourceListEnumSourcesA (MSI.@)
+ */
+UINT WINAPI MsiSourceListEnumSourcesA(LPCSTR szProductCodeOrPatch, LPCSTR szUserSid,
+ MSIINSTALLCONTEXT dwContext,
+ DWORD dwOptions, DWORD dwIndex,
+ LPSTR szSource, LPDWORD pcchSource)
+{
+ LPWSTR product = NULL;
+ LPWSTR usersid = NULL;
+ LPWSTR source = NULL;
+ DWORD len = 0;
+ UINT r = ERROR_INVALID_PARAMETER;
+ static DWORD index = 0;
+
+ TRACE("(%s, %s, %d, %d, %d, %p, %p)\n", debugstr_a(szProductCodeOrPatch),
+ debugstr_a(szUserSid), dwContext, dwOptions, dwIndex, szSource, pcchSource);
+
+ if (dwIndex == 0)
+ index = 0;
+
+ if (szSource && !pcchSource)
+ goto done;
+
+ if (dwIndex != index)
+ goto done;
+
+ if (szProductCodeOrPatch) product = strdupAtoW(szProductCodeOrPatch);
+ if (szUserSid) usersid = strdupAtoW(szUserSid);
+
+ r = MsiSourceListEnumSourcesW(product, usersid, dwContext, dwOptions,
+ dwIndex, NULL, &len);
+ if (r != ERROR_SUCCESS)
+ goto done;
+
+ source = msi_alloc(++len * sizeof(WCHAR));
+ if (!source)
+ {
+ r = ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ *source = '\0';
+ r = MsiSourceListEnumSourcesW(product, usersid, dwContext, dwOptions,
+ dwIndex, source, &len);
+ if (r != ERROR_SUCCESS)
+ goto done;
+
+ len = WideCharToMultiByte(CP_ACP, 0, source, -1, NULL, 0, NULL, NULL);
+ if (pcchSource && *pcchSource >= len)
+ WideCharToMultiByte(CP_ACP, 0, source, -1, szSource, len, NULL, NULL);
+ else if (szSource)
+ r = ERROR_MORE_DATA;
+
+ if (pcchSource)
+ *pcchSource = len - 1;
+
+done:
+ msi_free(product);
+ msi_free(usersid);
+ msi_free(source);
+
+ if (r == ERROR_SUCCESS)
+ {
+ if (szSource || !pcchSource) index++;
+ }
+ else if (dwIndex > index)
+ index = 0;
+
+ return r;
+}
+
+/******************************************************************
+ * MsiSourceListEnumSourcesW (MSI.@)
+ */
+UINT WINAPI MsiSourceListEnumSourcesW(LPCWSTR szProductCodeOrPatch, LPCWSTR szUserSid,
+ MSIINSTALLCONTEXT dwContext,
+ DWORD dwOptions, DWORD dwIndex,
+ LPWSTR szSource, LPDWORD pcchSource)
+{
+ WCHAR squished_pc[GUID_SIZE];
+ WCHAR name[32];
+ HKEY source = NULL;
+ HKEY subkey = NULL;
+ LONG res;
+ UINT r = ERROR_INVALID_PARAMETER;
+ static DWORD index = 0;
+
+ static const WCHAR format[] = {'%','d',0};
+
+ TRACE("(%s, %s, %d, %d, %d, %p, %p)\n", debugstr_w(szProductCodeOrPatch),
+ debugstr_w(szUserSid), dwContext, dwOptions, dwIndex, szSource, pcchSource);
+
+ if (dwIndex == 0)
+ index = 0;
+
+ if (!szProductCodeOrPatch || !squash_guid(szProductCodeOrPatch, squished_pc))
+ goto done;
+
+ if (szSource && !pcchSource)
+ goto done;
+
+ if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
+ goto done;
+
+ if ((dwOptions & MSISOURCETYPE_NETWORK) && (dwOptions & MSISOURCETYPE_URL))
+ goto done;
+
+ if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid)
+ goto done;
+
+ if (dwIndex != index)
+ goto done;
+
+ r = OpenSourceKey(szProductCodeOrPatch, &source,
+ dwOptions, dwContext, FALSE);
+ if (r != ERROR_SUCCESS)
+ goto done;
+
+ if (dwOptions & MSISOURCETYPE_NETWORK)
+ r = OpenNetworkSubkey(source, &subkey, FALSE);
+ else if (dwOptions & MSISOURCETYPE_URL)
+ r = OpenURLSubkey(source, &subkey, FALSE);
+
+ if (r != ERROR_SUCCESS)
+ {
+ r = ERROR_NO_MORE_ITEMS;
+ goto done;
+ }
+
+ sprintfW(name, format, dwIndex + 1);
+
+ res = RegQueryValueExW(subkey, name, 0, 0, (LPBYTE)szSource, pcchSource);
+ if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA)
+ r = ERROR_NO_MORE_ITEMS;
+
+done:
+ RegCloseKey(subkey);
+ RegCloseKey(source);
+
+ if (r == ERROR_SUCCESS)
+ {
+ if (szSource || !pcchSource) index++;
+ }
+ else if (dwIndex > index)
+ index = 0;
+
+ return r;
+}
+
+/******************************************************************
+ * MsiSourceListGetInfoA (MSI.@)
+ */
+UINT WINAPI MsiSourceListGetInfoA( LPCSTR szProduct, LPCSTR szUserSid,
+ MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
+ LPCSTR szProperty, LPSTR szValue,
+ LPDWORD pcchValue)
+{
+ UINT ret;
+ LPWSTR product = NULL;
+ LPWSTR usersid = NULL;
+ LPWSTR property = NULL;
+ LPWSTR value = NULL;
+ DWORD len = 0;
+
+ if (szValue && !pcchValue)
+ return ERROR_INVALID_PARAMETER;
+
+ if (szProduct) product = strdupAtoW(szProduct);
+ if (szUserSid) usersid = strdupAtoW(szUserSid);
+ if (szProperty) property = strdupAtoW(szProperty);
+
+ ret = MsiSourceListGetInfoW(product, usersid, dwContext, dwOptions,
+ property, NULL, &len);
+ if (ret != ERROR_SUCCESS)
+ goto done;
+
+ value = msi_alloc(++len * sizeof(WCHAR));
+ if (!value)
+ return ERROR_OUTOFMEMORY;
+
+ *value = '\0';
+ ret = MsiSourceListGetInfoW(product, usersid, dwContext, dwOptions,
+ property, value, &len);
+ if (ret != ERROR_SUCCESS)
+ goto done;
+
+ len = WideCharToMultiByte(CP_ACP, 0, value, -1, NULL, 0, NULL, NULL);
+ if (*pcchValue >= len)
+ WideCharToMultiByte(CP_ACP, 0, value, -1, szValue, len, NULL, NULL);
+ else if (szValue)
+ ret = ERROR_MORE_DATA;
+
+ *pcchValue = len - 1;
+
+done:
+ msi_free(product);
+ msi_free(usersid);
+ msi_free(property);
+ msi_free(value);
+ return ret;
+}
+
+/******************************************************************
+ * MsiSourceListGetInfoW (MSI.@)
+ */
+UINT WINAPI MsiSourceListGetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
+ MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
+ LPCWSTR szProperty, LPWSTR szValue,
+ LPDWORD pcchValue)
+{
+ WCHAR squished_pc[GUID_SIZE];
+ HKEY sourcekey, media;
+ LPWSTR source, ptr;
+ DWORD size;
+ UINT rc;
+
+ static const WCHAR mediapack[] = {
+ 'M','e','d','i','a','P','a','c','k','a','g','e',0};
+
+ TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szProperty));
+
+ if (!szProduct || !squash_guid(szProduct, squished_pc))
+ return ERROR_INVALID_PARAMETER;
+
+ if (szValue && !pcchValue)
+ return ERROR_INVALID_PARAMETER;
+
+ if (dwContext != MSIINSTALLCONTEXT_USERMANAGED &&
+ dwContext != MSIINSTALLCONTEXT_USERUNMANAGED &&
+ dwContext != MSIINSTALLCONTEXT_MACHINE)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!szProperty)
+ return ERROR_INVALID_PARAMETER;
+
+ if (szUserSid)
+ FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
+
+ rc = OpenSourceKey(szProduct, &sourcekey, dwOptions, dwContext, FALSE);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW ) ||
+ !strcmpW( szProperty, INSTALLPROPERTY_DISKPROMPTW ))
+ {
+ rc = OpenMediaSubkey(sourcekey, &media, FALSE);
+ if (rc != ERROR_SUCCESS)
+ {
+ RegCloseKey(sourcekey);
+ return ERROR_SUCCESS;
+ }
+
+ if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW ))
+ szProperty = mediapack;
+
+ RegQueryValueExW(media, szProperty, 0, 0, (LPBYTE)szValue, pcchValue);
+ RegCloseKey(media);
+ }
+ else if (!strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW ) ||
+ !strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDTYPEW ))
+ {
+ rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
+ 0, 0, NULL, &size);
+ if (rc != ERROR_SUCCESS)
+ {
+ RegCloseKey(sourcekey);
+ return ERROR_SUCCESS;
+ }
+
+ source = msi_alloc(size);
+ RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
+ 0, 0, (LPBYTE)source, &size);
+
+ if (!*source)
+ {
+ msi_free(source);
+ RegCloseKey(sourcekey);
+ return ERROR_SUCCESS;
+ }
+
+ if (!strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDTYPEW ))
+ {
+ if (*source != 'n' && *source != 'u' && *source != 'm')
+ {
+ msi_free(source);
+ RegCloseKey(sourcekey);
+ return ERROR_SUCCESS;
+ }
+
+ ptr = source;
+ source[1] = '\0';
+ }
+ else
+ {
+ ptr = strrchrW(source, ';');
+ if (!ptr)
+ ptr = source;
+ else
+ ptr++;
+ }
+
+ if (szValue)
+ {
+ if (strlenW(ptr) < *pcchValue)
+ lstrcpyW(szValue, ptr);
+ else
+ rc = ERROR_MORE_DATA;
+ }
+
+ *pcchValue = lstrlenW(ptr);
+ msi_free(source);
+ }
+ else if (!strcmpW( szProperty, INSTALLPROPERTY_PACKAGENAMEW ))
+ {
+ *pcchValue = *pcchValue * sizeof(WCHAR);
+ rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, 0,
+ (LPBYTE)szValue, pcchValue);
+ if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
+ {
+ *pcchValue = 0;
+ rc = ERROR_SUCCESS;
+ }
+ else
+ {
+ if (*pcchValue)
+ *pcchValue = (*pcchValue - 1) / sizeof(WCHAR);
+ if (szValue)
+ szValue[*pcchValue] = '\0';
+ }
+ }
+ else
+ {
+ FIXME("Unknown property %s\n",debugstr_w(szProperty));
+ rc = ERROR_UNKNOWN_PROPERTY;
+ }
+
+ RegCloseKey(sourcekey);
+ return rc;
+}
+
+/******************************************************************
+ * MsiSourceListSetInfoA (MSI.@)
+ */
+UINT WINAPI MsiSourceListSetInfoA(LPCSTR szProduct, LPCSTR szUserSid,
+ MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
+ LPCSTR szProperty, LPCSTR szValue)
+{
+ UINT ret;
+ LPWSTR product = NULL;
+ LPWSTR usersid = NULL;
+ LPWSTR property = NULL;
+ LPWSTR value = NULL;
+
+ if (szProduct) product = strdupAtoW(szProduct);
+ if (szUserSid) usersid = strdupAtoW(szUserSid);
+ if (szProperty) property = strdupAtoW(szProperty);
+ if (szValue) value = strdupAtoW(szValue);
+
+ ret = MsiSourceListSetInfoW(product, usersid, dwContext, dwOptions,
+ property, value);
+
+ msi_free(product);
+ msi_free(usersid);
+ msi_free(property);
+ msi_free(value);
+
+ return ret;
+}
+
+UINT msi_set_last_used_source(LPCWSTR product, LPCWSTR usersid,
+ MSIINSTALLCONTEXT context, DWORD options,
+ LPCWSTR value)
+{
+ HKEY source;
+ LPWSTR buffer;
+ WCHAR typechar;
+ DWORD size;
+ UINT r;
+ int index = 1;
+
+ static const WCHAR format[] = {'%','c',';','%','i',';','%','s',0};
+
+ if (options & MSISOURCETYPE_NETWORK)
+ typechar = 'n';
+ else if (options & MSISOURCETYPE_URL)
+ typechar = 'u';
+ else if (options & MSISOURCETYPE_MEDIA)
+ typechar = 'm';
+ else
+ return ERROR_INVALID_PARAMETER;
+
+ if (!(options & MSISOURCETYPE_MEDIA))
+ {
+ r = MsiSourceListAddSourceExW(product, usersid, context,
+ options, value, 0);
+ if (r != ERROR_SUCCESS)
+ return r;
+
+ index = 0;
+ while ((r = MsiSourceListEnumSourcesW(product, usersid, context, options,
+ index, NULL, NULL)) == ERROR_SUCCESS)
+ index++;
+
+ if (r != ERROR_NO_MORE_ITEMS)
+ return r;
+ }
+
+ size = (lstrlenW(format) + lstrlenW(value) + 7) * sizeof(WCHAR);
+ buffer = msi_alloc(size);
+ if (!buffer)
+ return ERROR_OUTOFMEMORY;
+
+ r = OpenSourceKey(product, &source, MSICODE_PRODUCT, context, FALSE);
+ if (r != ERROR_SUCCESS)
+ return r;
+
+ sprintfW(buffer, format, typechar, index, value);
+
+ size = (lstrlenW(buffer) + 1) * sizeof(WCHAR);
+ r = RegSetValueExW(source, INSTALLPROPERTY_LASTUSEDSOURCEW, 0,
+ REG_SZ, (LPBYTE)buffer, size);
+ msi_free(buffer);
+
+ RegCloseKey(source);
+ return r;
+}
+
+/******************************************************************
+ * MsiSourceListSetInfoW (MSI.@)
+ */
+UINT WINAPI MsiSourceListSetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
+ MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
+ LPCWSTR szProperty, LPCWSTR szValue)
+{
+ WCHAR squished_pc[GUID_SIZE];
+ HKEY sourcekey, media;
+ LPCWSTR property;
+ UINT rc;
+
+ static const WCHAR media_package[] = {
+ 'M','e','d','i','a','P','a','c','k','a','g','e',0
+ };
+
+ TRACE("%s %s %x %x %s %s\n", debugstr_w(szProduct), debugstr_w(szUserSid),
+ dwContext, dwOptions, debugstr_w(szProperty), debugstr_w(szValue));
+
+ if (!szProduct || !squash_guid(szProduct, squished_pc))
+ return ERROR_INVALID_PARAMETER;
+
+ if (!szProperty)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!szValue)
+ return ERROR_UNKNOWN_PROPERTY;
+
+ if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid)
+ return ERROR_INVALID_PARAMETER;
+
+ if (dwOptions & MSICODE_PATCH)
+ {
+ FIXME("Unhandled options MSICODE_PATCH\n");
+ return ERROR_UNKNOWN_PATCH;
+ }
+
+ property = szProperty;
+ if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW ))
+ property = media_package;
+
+ rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ if (strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW ) &&
+ dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL))
+ {
+ RegCloseKey(sourcekey);
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW ) ||
+ !strcmpW( szProperty, INSTALLPROPERTY_DISKPROMPTW ))
+ {
+ rc = OpenMediaSubkey(sourcekey, &media, TRUE);
+ if (rc == ERROR_SUCCESS)
+ {
+ rc = msi_reg_set_val_str(media, property, szValue);
+ RegCloseKey(media);
+ }
+ }
+ else if (!strcmpW( szProperty, INSTALLPROPERTY_PACKAGENAMEW ))
+ {
+ DWORD size = (lstrlenW(szValue) + 1) * sizeof(WCHAR);
+ rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0,
+ REG_SZ, (const BYTE *)szValue, size);
+ if (rc != ERROR_SUCCESS)
+ rc = ERROR_UNKNOWN_PROPERTY;
+ }
+ else if (!strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW ))
+ {
+ if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
+ rc = ERROR_INVALID_PARAMETER;
+ else
+ rc = msi_set_last_used_source(szProduct, szUserSid, dwContext,
+ dwOptions, szValue);
+ }
+ else
+ rc = ERROR_UNKNOWN_PROPERTY;
+
+ RegCloseKey(sourcekey);
+ return rc;
+}
+
+/******************************************************************
+ * MsiSourceListAddSourceW (MSI.@)
+ */
+UINT WINAPI MsiSourceListAddSourceW( LPCWSTR szProduct, LPCWSTR szUserName,
+ DWORD dwReserved, LPCWSTR szSource)
+{
+ WCHAR squished_pc[GUID_SIZE];
+ INT ret;
+ LPWSTR sidstr = NULL;
+ DWORD sidsize = 0;
+ DWORD domsize = 0;
+ DWORD context;
+ HKEY hkey = 0;
+ UINT r;
+
+ TRACE("%s %s %s\n", debugstr_w(szProduct), debugstr_w(szUserName), debugstr_w(szSource));
+
+ if (!szSource || !*szSource)
+ return ERROR_INVALID_PARAMETER;
+
+ if (dwReserved != 0)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!szProduct || !squash_guid(szProduct, squished_pc))
+ return ERROR_INVALID_PARAMETER;
+
+ if (!szUserName || !*szUserName)
+ context = MSIINSTALLCONTEXT_MACHINE;
+ else
+ {
+ if (LookupAccountNameW(NULL, szUserName, NULL, &sidsize, NULL, &domsize, NULL))
+ {
+ PSID psid = msi_alloc(sidsize);
+
+ if (LookupAccountNameW(NULL, szUserName, psid, &sidsize, NULL, &domsize, NULL))
+ ConvertSidToStringSidW(psid, &sidstr);
+
+ msi_free(psid);
+ }
+
+ r = MSIREG_OpenProductKey(szProduct, NULL,
+ MSIINSTALLCONTEXT_USERMANAGED, &hkey, FALSE);
+ if (r == ERROR_SUCCESS)
+ context = MSIINSTALLCONTEXT_USERMANAGED;
+ else
+ {
+ r = MSIREG_OpenProductKey(szProduct, NULL,
+ MSIINSTALLCONTEXT_USERUNMANAGED,
+ &hkey, FALSE);
+ if (r != ERROR_SUCCESS)
+ return ERROR_UNKNOWN_PRODUCT;
+
+ context = MSIINSTALLCONTEXT_USERUNMANAGED;
+ }
+
+ RegCloseKey(hkey);
+ }
+
+ ret = MsiSourceListAddSourceExW(szProduct, sidstr,
+ context, MSISOURCETYPE_NETWORK, szSource, 0);
+
+ if (sidstr)
+ LocalFree(sidstr);
+
+ return ret;
+}
+
+/******************************************************************
+ * MsiSourceListAddSourceA (MSI.@)
+ */
+UINT WINAPI MsiSourceListAddSourceA( LPCSTR szProduct, LPCSTR szUserName,
+ DWORD dwReserved, LPCSTR szSource)
+{
+ INT ret;
+ LPWSTR szwproduct;
+ LPWSTR szwusername;
+ LPWSTR szwsource;
+
+ szwproduct = strdupAtoW( szProduct );
+ szwusername = strdupAtoW( szUserName );
+ szwsource = strdupAtoW( szSource );
+
+ ret = MsiSourceListAddSourceW(szwproduct, szwusername, dwReserved, szwsource);
+
+ msi_free(szwproduct);
+ msi_free(szwusername);
+ msi_free(szwsource);
+
+ return ret;
+}
+
+/******************************************************************
+ * MsiSourceListAddSourceExA (MSI.@)
+ */
+UINT WINAPI MsiSourceListAddSourceExA(LPCSTR szProduct, LPCSTR szUserSid,
+ MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCSTR szSource, DWORD dwIndex)
+{
+ UINT ret;
+ LPWSTR product, usersid, source;
+
+ product = strdupAtoW(szProduct);
+ usersid = strdupAtoW(szUserSid);
+ source = strdupAtoW(szSource);
+
+ ret = MsiSourceListAddSourceExW(product, usersid, dwContext,
+ dwOptions, source, dwIndex);
+
+ msi_free(product);
+ msi_free(usersid);
+ msi_free(source);
+
+ return ret;
+}
+
+static void free_source_list(struct list *sourcelist)
+{
+ while (!list_empty(sourcelist))
+ {
+ media_info *info = LIST_ENTRY(list_head(sourcelist), media_info, entry);
+ list_remove(&info->entry);
+ msi_free(info->path);
+ msi_free(info);
+ }
+}
+
+static void add_source_to_list(struct list *sourcelist, media_info *info,
+ DWORD *index)
+{
+ media_info *iter;
+ BOOL found = FALSE;
+ static const WCHAR fmt[] = {'%','i',0};
+
+ if (index) *index = 0;
+
+ if (list_empty(sourcelist))
+ {
+ list_add_head(sourcelist, &info->entry);
+ return;
+ }
+
+ LIST_FOR_EACH_ENTRY(iter, sourcelist, media_info, entry)
+ {
+ if (!found && info->index < iter->index)
+ {
+ found = TRUE;
+ list_add_before(&iter->entry, &info->entry);
+ }
+
+ /* update the rest of the list */
+ if (found)
+ sprintfW(iter->szIndex, fmt, ++iter->index);
+ else if (index)
+ (*index)++;
+ }
+
+ if (!found)
+ list_add_after(&iter->entry, &info->entry);
+}
+
+static UINT fill_source_list(struct list *sourcelist, HKEY sourcekey, DWORD *count)
+{
+ UINT r = ERROR_SUCCESS;
+ DWORD index = 0;
+ WCHAR name[10];
+ DWORD size, val_size;
+ media_info *entry;
+
+ *count = 0;
+
+ while (r == ERROR_SUCCESS)
+ {
+ size = sizeof(name) / sizeof(name[0]);
+ r = RegEnumValueW(sourcekey, index, name, &size, NULL, NULL, NULL, &val_size);
+ if (r != ERROR_SUCCESS)
+ return r;
+
+ entry = msi_alloc(sizeof(media_info));
+ if (!entry)
+ goto error;
+
+ entry->path = msi_alloc(val_size);
+ if (!entry->path)
+ {
+ msi_free(entry);
+ goto error;
+ }
+
+ lstrcpyW(entry->szIndex, name);
+ entry->index = atoiW(name);
+
+ size++;
+ r = RegEnumValueW(sourcekey, index, name, &size, NULL,
+ NULL, (LPBYTE)entry->path, &val_size);
+ if (r != ERROR_SUCCESS)
+ {
+ msi_free(entry->path);
+ msi_free(entry);
+ goto error;
+ }
+
+ index = ++(*count);
+ add_source_to_list(sourcelist, entry, NULL);
+ }
+
+error:
+ *count = -1;
+ free_source_list(sourcelist);
+ return ERROR_OUTOFMEMORY;
+}
+
+/******************************************************************
+ * MsiSourceListAddSourceExW (MSI.@)
+ */
+UINT WINAPI MsiSourceListAddSourceExW( LPCWSTR szProduct, LPCWSTR szUserSid,
+ MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szSource,
+ DWORD dwIndex)
+{
+ HKEY sourcekey;
+ HKEY typekey;
+ UINT rc;
+ struct list sourcelist;
+ media_info *info;
+ WCHAR squished_pc[GUID_SIZE];
+ WCHAR name[10];
+ LPWSTR source;
+ LPCWSTR postfix;
+ DWORD size, count;
+ DWORD index;
+
+ static const WCHAR fmt[] = {'%','i',0};
+
+ TRACE("%s %s %x %x %s %i\n", debugstr_w(szProduct), debugstr_w(szUserSid),
+ dwContext, dwOptions, debugstr_w(szSource), dwIndex);
+
+ if (!szProduct || !squash_guid(szProduct, squished_pc))
+ return ERROR_INVALID_PARAMETER;
+
+ if (!szSource || !*szSource)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)))
+ return ERROR_INVALID_PARAMETER;
+
+ if (dwOptions & MSICODE_PATCH)
+ {
+ FIXME("Unhandled options MSICODE_PATCH\n");
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ if (szUserSid && (dwContext & MSIINSTALLCONTEXT_MACHINE))
+ return ERROR_INVALID_PARAMETER;
+
+ rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ if (dwOptions & MSISOURCETYPE_NETWORK)
+ rc = OpenNetworkSubkey(sourcekey, &typekey, TRUE);
+ else if (dwOptions & MSISOURCETYPE_URL)
+ rc = OpenURLSubkey(sourcekey, &typekey, TRUE);
+ else if (dwOptions & MSISOURCETYPE_MEDIA)
+ rc = OpenMediaSubkey(sourcekey, &typekey, TRUE);
+ else
+ {
+ ERR("unknown media type: %08x\n", dwOptions);
+ RegCloseKey(sourcekey);
+ return ERROR_FUNCTION_FAILED;
+ }
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("can't open subkey %u\n", rc);
+ RegCloseKey(sourcekey);
+ return rc;
+ }
+
+ postfix = (dwOptions & MSISOURCETYPE_NETWORK) ? szBackSlash : szForwardSlash;
+ if (szSource[lstrlenW(szSource) - 1] == *postfix)
+ source = strdupW(szSource);
+ else
+ {
+ size = lstrlenW(szSource) + 2;
+ source = msi_alloc(size * sizeof(WCHAR));
+ lstrcpyW(source, szSource);
+ lstrcatW(source, postfix);
+ }
+
+ list_init(&sourcelist);
+ rc = fill_source_list(&sourcelist, typekey, &count);
+ if (rc != ERROR_NO_MORE_ITEMS)
+ return rc;
+
+ size = (lstrlenW(source) + 1) * sizeof(WCHAR);
+
+ if (count == 0)
+ {
+ rc = RegSetValueExW(typekey, szOne, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
+ goto done;
+ }
+ else if (dwIndex > count || dwIndex == 0)
+ {
+ sprintfW(name, fmt, count + 1);
+ rc = RegSetValueExW(typekey, name, 0, REG_EXPAND_SZ, (LPBYTE)source, size);
+ goto done;
+ }
+ else
+ {
+ sprintfW(name, fmt, dwIndex);
+ info = msi_alloc(sizeof(media_info));
+ if (!info)
+ {
+ rc = ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ info->path = strdupW(source);
+ lstrcpyW(info->szIndex, name);
+ info->index = dwIndex;
+ add_source_to_list(&sourcelist, info, &index);
+
+ LIST_FOR_EACH_ENTRY(info, &sourcelist, media_info, entry)
+ {
+ if (info->index < index)
+ continue;
+
+ size = (lstrlenW(info->path) + 1) * sizeof(WCHAR);
+ rc = RegSetValueExW(typekey, info->szIndex, 0,
+ REG_EXPAND_SZ, (LPBYTE)info->path, size);
+ if (rc != ERROR_SUCCESS)
+ goto done;
+ }
+ }
+
+done:
+ free_source_list(&sourcelist);
+ msi_free(source);
+ RegCloseKey(typekey);
+ RegCloseKey(sourcekey);
+ return rc;
+}
+
+/******************************************************************
+ * MsiSourceListAddMediaDiskA (MSI.@)
+ */
+UINT WINAPI MsiSourceListAddMediaDiskA(LPCSTR szProduct, LPCSTR szUserSid,
+ MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId,
+ LPCSTR szVolumeLabel, LPCSTR szDiskPrompt)
+{
+ UINT r;
+ LPWSTR product = NULL;
+ LPWSTR usersid = NULL;
+ LPWSTR volume = NULL;
+ LPWSTR prompt = NULL;
+
+ if (szProduct) product = strdupAtoW(szProduct);
+ if (szUserSid) usersid = strdupAtoW(szUserSid);
+ if (szVolumeLabel) volume = strdupAtoW(szVolumeLabel);
+ if (szDiskPrompt) prompt = strdupAtoW(szDiskPrompt);
+
+ r = MsiSourceListAddMediaDiskW(product, usersid, dwContext, dwOptions,
+ dwDiskId, volume, prompt);
+
+ msi_free(product);
+ msi_free(usersid);
+ msi_free(volume);
+ msi_free(prompt);
+
+ return r;
+}
+
+/******************************************************************
+ * MsiSourceListAddMediaDiskW (MSI.@)
+ */
+UINT WINAPI MsiSourceListAddMediaDiskW(LPCWSTR szProduct, LPCWSTR szUserSid,
+ MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId,
+ LPCWSTR szVolumeLabel, LPCWSTR szDiskPrompt)
+{
+ HKEY sourcekey;
+ HKEY mediakey;
+ UINT rc;
+ WCHAR szIndex[10];
+ WCHAR squished_pc[GUID_SIZE];
+ LPWSTR buffer;
+ DWORD size;
+
+ static const WCHAR fmt[] = {'%','i',0};
+
+ TRACE("%s %s %x %x %i %s %s\n", debugstr_w(szProduct),
+ debugstr_w(szUserSid), dwContext, dwOptions, dwDiskId,
+ debugstr_w(szVolumeLabel), debugstr_w(szDiskPrompt));
+
+ if (!szProduct || !squash_guid(szProduct, squished_pc))
+ return ERROR_INVALID_PARAMETER;
+
+ if (dwOptions != MSICODE_PRODUCT && dwOptions != MSICODE_PATCH)
+ return ERROR_INVALID_PARAMETER;
+
+ if ((szVolumeLabel && !*szVolumeLabel) || (szDiskPrompt && !*szDiskPrompt))
+ return ERROR_INVALID_PARAMETER;
+
+ if ((dwContext & MSIINSTALLCONTEXT_MACHINE) && szUserSid)
+ return ERROR_INVALID_PARAMETER;
+
+ if (dwOptions & MSICODE_PATCH)
+ {
+ FIXME("Unhandled options MSICODE_PATCH\n");
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ OpenMediaSubkey(sourcekey, &mediakey, TRUE);
+
+ sprintfW(szIndex, fmt, dwDiskId);
+
+ size = 2;
+ if (szVolumeLabel) size += lstrlenW(szVolumeLabel);
+ if (szDiskPrompt) size += lstrlenW(szDiskPrompt);
+
+ size *= sizeof(WCHAR);
+ buffer = msi_alloc(size);
+ *buffer = '\0';
+
+ if (szVolumeLabel) lstrcpyW(buffer, szVolumeLabel);
+ lstrcatW(buffer, szSemiColon);
+ if (szDiskPrompt) lstrcatW(buffer, szDiskPrompt);
+
+ RegSetValueExW(mediakey, szIndex, 0, REG_SZ, (LPBYTE)buffer, size);
+ msi_free(buffer);
+
+ RegCloseKey(sourcekey);
+ RegCloseKey(mediakey);
+
+ return ERROR_SUCCESS;
+}
+
+/******************************************************************
+ * MsiSourceListClearAllA (MSI.@)
+ */
+UINT WINAPI MsiSourceListClearAllA( LPCSTR szProduct, LPCSTR szUserName, DWORD dwReserved )
+{
+ FIXME("(%s %s %d)\n", debugstr_a(szProduct), debugstr_a(szUserName), dwReserved);
+ return ERROR_SUCCESS;
+}
+
+/******************************************************************
+ * MsiSourceListClearAllW (MSI.@)
+ */
+UINT WINAPI MsiSourceListClearAllW( LPCWSTR szProduct, LPCWSTR szUserName, DWORD dwReserved )
+{
+ FIXME("(%s %s %d)\n", debugstr_w(szProduct), debugstr_w(szUserName), dwReserved);
+ return ERROR_SUCCESS;
+}
+
+/******************************************************************
+ * MsiSourceListClearAllExA (MSI.@)
+ */
+UINT WINAPI MsiSourceListClearAllExA( LPCSTR szProduct, LPCSTR szUserSid,
+ MSIINSTALLCONTEXT dwContext, DWORD dwOptions )
+{
+ FIXME("(%s %s %d %08x)\n", debugstr_a(szProduct), debugstr_a(szUserSid),
+ dwContext, dwOptions);
+ return ERROR_SUCCESS;
+}
+
+/******************************************************************
+ * MsiSourceListClearAllExW (MSI.@)
+ */
+UINT WINAPI MsiSourceListClearAllExW( LPCWSTR szProduct, LPCWSTR szUserSid,
+ MSIINSTALLCONTEXT dwContext, DWORD dwOptions )
+{
+ FIXME("(%s %s %d %08x)\n", debugstr_w(szProduct), debugstr_w(szUserSid),
+ dwContext, dwOptions);
+ return ERROR_SUCCESS;
+}
+
+/******************************************************************
+ * MsiSourceListClearSourceA (MSI.@)
+ */
+UINT WINAPI MsiSourceListClearSourceA(LPCSTR szProductCodeOrPatchCode, LPCSTR szUserSid,
+ MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
+ LPCSTR szSource)
+{
+ FIXME("(%s %s %x %x %s)\n", debugstr_a(szProductCodeOrPatchCode), debugstr_a(szUserSid),
+ dwContext, dwOptions, debugstr_a(szSource));
+ return ERROR_SUCCESS;
+}
+
+/******************************************************************
+ * MsiSourceListClearSourceW (MSI.@)
+ */
+UINT WINAPI MsiSourceListClearSourceW(LPCWSTR szProductCodeOrPatchCode, LPCWSTR szUserSid,
+ MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
+ LPCWSTR szSource)
+{
+ FIXME("(%s %s %x %x %s)\n", debugstr_w(szProductCodeOrPatchCode), debugstr_w(szUserSid),
+ dwContext, dwOptions, debugstr_w(szSource));
+ return ERROR_SUCCESS;
+}