| /* |
| * 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 |
| |
| #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) |
| { |
| static const WCHAR fmt[] = {'#','%','d',0}; |
| WCHAR squashed_pc[SQUASHED_GUID_SIZE], convert[11]; |
| WCHAR *value = NULL, *data = NULL, *ptr, *ptr2; |
| HKEY source, media; |
| DWORD valuesz, datasz = 0, type, numvals, size; |
| LONG res; |
| UINT r; |
| static DWORD index = 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, squashed_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; |
| } |
| |
| 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) |
| { |
| static const WCHAR format[] = {'%','d',0}; |
| WCHAR squashed_pc[SQUASHED_GUID_SIZE], name[32]; |
| HKEY source = NULL, subkey = NULL; |
| LONG res; |
| UINT r = ERROR_INVALID_PARAMETER; |
| static DWORD index = 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, squashed_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) |
| { |
| static const WCHAR mediapack[] = {'M','e','d','i','a','P','a','c','k','a','g','e',0}; |
| WCHAR *source, *ptr, squashed_pc[SQUASHED_GUID_SIZE]; |
| HKEY sourcekey, media; |
| DWORD size; |
| UINT rc; |
| |
| TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szProperty)); |
| |
| if (!szProduct || !squash_guid( szProduct, squashed_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) |
| { |
| msi_free(buffer); |
| 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) |
| { |
| static const WCHAR media_package[] = {'M','e','d','i','a','P','a','c','k','a','g','e',0}; |
| WCHAR squashed_pc[SQUASHED_GUID_SIZE]; |
| HKEY sourcekey, media; |
| LPCWSTR property; |
| UINT rc; |
| |
| 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, squashed_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 *sidstr = NULL, squashed_pc[SQUASHED_GUID_SIZE]; |
| INT ret; |
| DWORD sidsize = 0, domsize = 0, 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, squashed_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) |
| { |
| static const WCHAR fmt[] = {'%','i',0}; |
| HKEY sourcekey, typekey; |
| UINT rc; |
| struct list sourcelist; |
| media_info *info; |
| WCHAR *source, squashed_pc[SQUASHED_GUID_SIZE], name[10]; |
| LPCWSTR postfix; |
| DWORD size, count, index; |
| |
| 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, squashed_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) |
| goto done; |
| |
| 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) |
| { |
| static const WCHAR fmt[] = {'%','i',0}; |
| HKEY sourcekey, mediakey; |
| UINT rc; |
| WCHAR *buffer, squashed_pc[SQUASHED_GUID_SIZE], szIndex[10]; |
| DWORD size; |
| |
| 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, squashed_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; |
| } |