/* * 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 #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; static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0}; if (context == MSIINSTALLCONTEXT_USERUNMANAGED) { if (dwOptions == MSICODE_PATCH) rc = MSIREG_OpenUserPatchesKey(szProduct, &rootkey, create); else rc = MSIREG_OpenUserProductsKey(szProduct, &rootkey, create); } else if (context == MSIINSTALLCONTEXT_USERMANAGED) { if (dwOptions == MSICODE_PATCH) rc = MSIREG_OpenUserPatchesKey(szProduct, &rootkey, create); else rc = MSIREG_OpenLocalManagedProductKey(szProduct, &rootkey, create); } else if (context == MSIINSTALLCONTEXT_MACHINE) { if (dwOptions == MSICODE_PATCH) rc = MSIREG_OpenPatchesKey(szProduct, &rootkey, create); else rc = MSIREG_OpenProductsKey(szProduct, &rootkey, create); } if (rc) { 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; } /****************************************************************** * MsiSourceListEnumSourcesA (MSI.@) */ UINT WINAPI MsiSourceListEnumSourcesA(LPCSTR szProductCodeOrPatch, LPCSTR szUserSid, MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwIndex, LPSTR szSource, LPDWORD pcchSource) { FIXME("(%s, %s, %d, %d, %d, %p, %p): stub!\n", szProductCodeOrPatch, szUserSid, dwContext, dwOptions, dwIndex, szSource, pcchSource); return ERROR_CALL_NOT_IMPLEMENTED; } /****************************************************************** * 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) { HKEY sourcekey; UINT rc; TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szProperty)); if (!szProduct || !*szProduct) return ERROR_INVALID_PARAMETER; if (lstrlenW(szProduct) != GUID_SIZE - 1 || (szProduct[0] != '{' && szProduct[GUID_SIZE - 2] != '}')) 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)); if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED) FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n"); rc = OpenSourceKey(szProduct, &sourcekey, dwOptions, dwContext, FALSE); if (rc != ERROR_SUCCESS) return rc; if (strcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) == 0) { HKEY key; rc = OpenMediaSubkey(sourcekey, &key, FALSE); if (rc == ERROR_SUCCESS) rc = RegQueryValueExW(key, INSTALLPROPERTY_MEDIAPACKAGEPATHW, 0, 0, (LPBYTE)szValue, pcchValue); if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA) rc = ERROR_UNKNOWN_PROPERTY; RegCloseKey(key); } else if (strcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW) ==0) { HKEY key; rc = OpenMediaSubkey(sourcekey, &key, FALSE); if (rc == ERROR_SUCCESS) rc = RegQueryValueExW(key, INSTALLPROPERTY_DISKPROMPTW, 0, 0, (LPBYTE)szValue, pcchValue); if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA) rc = ERROR_UNKNOWN_PROPERTY; RegCloseKey(key); } else if (strcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW)==0) { LPWSTR buffer; DWORD size = 0; RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0, NULL, &size); if (size == 0) rc = ERROR_UNKNOWN_PROPERTY; else { LPWSTR ptr; buffer = msi_alloc(size); rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0, (LPBYTE)buffer,&size); ptr = strchrW(buffer,';'); if (ptr) ptr = strchrW(ptr+1,';'); if (!ptr) rc = ERROR_UNKNOWN_PROPERTY; else { ptr ++; lstrcpynW(szValue, ptr, *pcchValue); if (lstrlenW(ptr) > *pcchValue) { *pcchValue = lstrlenW(ptr)+1; rc = ERROR_MORE_DATA; } else rc = ERROR_SUCCESS; } msi_free(buffer); } } else if (strcmpW(INSTALLPROPERTY_LASTUSEDTYPEW, szProperty)==0) { LPWSTR buffer; DWORD size = 0; RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0, NULL, &size); if (size == 0) rc = ERROR_UNKNOWN_PROPERTY; else { buffer = msi_alloc(size); rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0, (LPBYTE)buffer,&size); if (*pcchValue < 1) { rc = ERROR_MORE_DATA; *pcchValue = 1; } else { szValue[0] = buffer[0]; rc = ERROR_SUCCESS; } msi_free(buffer); } } else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0) { *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; } /****************************************************************** * MsiSourceListSetInfoW (MSI.@) */ UINT WINAPI MsiSourceListSetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid, MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szProperty, LPCWSTR szValue) { HKEY sourcekey; 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 || lstrlenW(szProduct) > 39) return ERROR_INVALID_PARAMETER; if (dwOptions & MSICODE_PATCH) { FIXME("Unhandled options MSICODE_PATCH\n"); return ERROR_FUNCTION_FAILED; } if (szUserSid) FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid)); if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED) FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n"); rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, TRUE); if (rc != ERROR_SUCCESS) return ERROR_UNKNOWN_PRODUCT; if (strcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) == 0) { HKEY key; DWORD size = lstrlenW(szValue)*sizeof(WCHAR); rc = OpenMediaSubkey(sourcekey, &key, FALSE); if (rc == ERROR_SUCCESS) rc = RegSetValueExW(key, INSTALLPROPERTY_MEDIAPACKAGEPATHW, 0, REG_SZ, (const BYTE *)szValue, size); if (rc != ERROR_SUCCESS) rc = ERROR_UNKNOWN_PROPERTY; RegCloseKey(key); } else if (strcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW) == 0) { HKEY key; DWORD size = lstrlenW(szValue)*sizeof(WCHAR); rc = OpenMediaSubkey(sourcekey, &key, FALSE); if (rc == ERROR_SUCCESS) rc = RegSetValueExW(key, INSTALLPROPERTY_DISKPROMPTW, 0, REG_SZ, (const BYTE *)szValue, size); if (rc != ERROR_SUCCESS) rc = ERROR_UNKNOWN_PROPERTY; RegCloseKey(key); } else if (strcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW)==0) { LPWSTR buffer = NULL; DWORD size; WCHAR typechar = 'n'; static const WCHAR LastUsedSource_Fmt[] = {'%','c',';','%','i',';','%','s',0}; /* make sure the source is registered */ MsiSourceListAddSourceExW(szProduct, szUserSid, dwContext, dwOptions, szValue, 0); if (dwOptions & MSISOURCETYPE_NETWORK) typechar = 'n'; else if (dwOptions & MSISOURCETYPE_URL) typechar = 'u'; else if (dwOptions & MSISOURCETYPE_MEDIA) typechar = 'm'; else ERR("Unknown source type! %x\n", dwOptions); size = (lstrlenW(szValue)+5)*sizeof(WCHAR); buffer = msi_alloc(size); sprintfW(buffer, LastUsedSource_Fmt, typechar, 1, szValue); rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size); if (rc != ERROR_SUCCESS) rc = ERROR_UNKNOWN_PROPERTY; msi_free( buffer ); } else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0) { DWORD size = lstrlenW(szValue)*sizeof(WCHAR); rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, REG_SZ, (const BYTE *)szValue, size); if (rc != ERROR_SUCCESS) rc = ERROR_UNKNOWN_PROPERTY; } else { FIXME("Unknown property %s\n",debugstr_w(szProperty)); rc = ERROR_UNKNOWN_PROPERTY; } RegCloseKey(sourcekey); return rc; } /****************************************************************** * MsiSourceListAddSourceW (MSI.@) */ UINT WINAPI MsiSourceListAddSourceW( LPCWSTR szProduct, LPCWSTR szUserName, DWORD dwReserved, LPCWSTR szSource) { INT ret; LPWSTR sidstr = NULL; DWORD sidsize = 0; DWORD domsize = 0; TRACE("%s %s %s\n", debugstr_w(szProduct), debugstr_w(szUserName), debugstr_w(szSource)); 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); } ret = MsiSourceListAddSourceExW(szProduct, sidstr, MSIINSTALLCONTEXT_USERMANAGED, 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, 0, 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) { media_info *iter; BOOL found = FALSE; static const WCHAR fmt[] = {'%','i',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); } 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) 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) goto error; index = ++(*count); add_source_to_list(sourcelist, entry); } 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; static const WCHAR fmt[] = {'%','i',0}; static const WCHAR one[] = {'1',0}; static const WCHAR backslash[] = {'\\',0}; static const WCHAR forwardslash[] = {'/',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) FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid)); 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; } postfix = (dwOptions & MSISOURCETYPE_NETWORK) ? backslash : forwardslash; 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, one, 0, REG_EXPAND_SZ, (LPBYTE)source, size); goto done; } else if (dwIndex > count) { sprintfW(name, fmt, count + 1); rc = RegSetValueExW(typekey, name, 0, REG_EXPAND_SZ, (LPBYTE)source, size); goto done; } else { /* add to the end of the list */ if (dwIndex == 0) dwIndex = count + 1; 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); LIST_FOR_EACH_ENTRY(info, &sourcelist, media_info, entry) { 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; } /****************************************************************** * MsiSourceListAddMediaDisk(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]; static const WCHAR fmt[] = {'%','i',0}; static const WCHAR disk_fmt[] = {'%','s',';','%','s',0}; static const WCHAR empty[1] = {0}; LPCWSTR pt1,pt2; LPWSTR buffer; 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 || lstrlenW(szProduct) > 39) return ERROR_INVALID_PARAMETER; if (dwOptions & MSICODE_PATCH) { FIXME("Unhandled options MSICODE_PATCH\n"); return ERROR_FUNCTION_FAILED; } if (szUserSid) FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid)); if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED) FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n"); rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, TRUE); if (rc != ERROR_SUCCESS) return ERROR_UNKNOWN_PRODUCT; OpenMediaSubkey(sourcekey,&mediakey,TRUE); sprintfW(szIndex,fmt,dwDiskId); size = 2; if (szVolumeLabel) { size +=lstrlenW(szVolumeLabel); pt1 = szVolumeLabel; } else pt1 = empty; if (szDiskPrompt) { size +=lstrlenW(szDiskPrompt); pt2 = szDiskPrompt; } else pt2 = empty; size *=sizeof(WCHAR); buffer = msi_alloc(size); sprintfW(buffer,disk_fmt,pt1,pt2); 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; }