diff options
Diffstat (limited to 'libmsi/source.c')
-rw-r--r-- | libmsi/source.c | 1363 |
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; +} |