| /* |
| * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "action.h" |
| #include "sddl.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(msi); |
| |
| /* |
| * These apis are defined in MSI 3.0 |
| */ |
| |
| typedef struct tagMediaInfo |
| { |
| LPWSTR path; |
| WCHAR szIndex[10]; |
| WCHAR type; |
| } media_info; |
| |
| static UINT OpenSourceKey(LPCWSTR szProduct, HKEY* key, BOOL user, BOOL create) |
| { |
| HKEY rootkey = 0; |
| UINT rc; |
| static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0}; |
| |
| if (user) |
| rc = MSIREG_OpenUserProductsKey(szProduct, &rootkey, create); |
| else |
| rc = MSIREG_OpenProductsKey(szProduct, &rootkey, create); |
| |
| if (rc) |
| return rc; |
| |
| if (create) |
| rc = RegCreateKeyW(rootkey, szSourceList, key); |
| else |
| rc = RegOpenKeyW(rootkey,szSourceList, key); |
| |
| 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; |
| } |
| |
| |
| static UINT find_given_source(HKEY key, LPCWSTR szSource, media_info *ss) |
| { |
| DWORD index = 0; |
| WCHAR szIndex[10]; |
| DWORD size; |
| DWORD val_size; |
| LPWSTR val; |
| UINT rc = ERROR_SUCCESS; |
| |
| while (rc == ERROR_SUCCESS) |
| { |
| val = NULL; |
| val_size = 0; |
| size = sizeof(szIndex)/sizeof(szIndex[0]); |
| rc = RegEnumValueW(key, index, szIndex, &size, NULL, NULL, NULL, &val_size); |
| if (rc != ERROR_NO_MORE_ITEMS) |
| { |
| val = msi_alloc(val_size); |
| RegEnumValueW(key, index, szIndex, &size, NULL, NULL, (LPBYTE)val, |
| &val_size); |
| if (lstrcmpiW(szSource,val)==0) |
| { |
| ss->path = val; |
| strcpyW(ss->szIndex,szIndex); |
| break; |
| } |
| else |
| strcpyW(ss->szIndex,szIndex); |
| |
| msi_free(val); |
| index ++; |
| } |
| } |
| return rc; |
| } |
| |
| /****************************************************************** |
| * 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 || lstrlenW(szProduct) > 39) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (szValue && !pcchValue) |
| 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"); |
| |
| if (dwContext == MSIINSTALLCONTEXT_MACHINE) |
| rc = OpenSourceKey(szProduct, &sourcekey, FALSE, FALSE); |
| else |
| rc = OpenSourceKey(szProduct, &sourcekey, TRUE, FALSE); |
| |
| if (rc != ERROR_SUCCESS) |
| return ERROR_UNKNOWN_PRODUCT; |
| |
| 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) |
| { |
| rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, 0, |
| (LPBYTE)szValue, pcchValue); |
| if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA) |
| rc = ERROR_UNKNOWN_PROPERTY; |
| } |
| 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 %lx %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"); |
| |
| if (dwContext == MSIINSTALLCONTEXT_MACHINE) |
| rc = OpenSourceKey(szProduct, &sourcekey, FALSE, TRUE); |
| else |
| rc = OpenSourceKey(szProduct, &sourcekey, TRUE, 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, (LPBYTE)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, (LPBYTE)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! 0x%lx\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, (LPBYTE)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; |
| |
| TRACE("%s %s %s\n", debugstr_w(szProduct), debugstr_w(szUserName), debugstr_w(szSource)); |
| |
| if (LookupAccountNameW(NULL, szUserName, NULL, &sidsize, NULL, NULL, NULL)) |
| { |
| PSID psid = msi_alloc(sidsize); |
| |
| if (LookupAccountNameW(NULL, szUserName, psid, &sidsize, NULL, NULL, 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; |
| } |
| |
| /****************************************************************** |
| * MsiSourceListAddSourceExW (MSI.@) |
| */ |
| UINT WINAPI MsiSourceListAddSourceExW( LPCWSTR szProduct, LPCWSTR szUserSid, |
| MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szSource, |
| DWORD dwIndex) |
| { |
| HKEY sourcekey; |
| HKEY typekey; |
| UINT rc; |
| media_info source_struct; |
| |
| TRACE("%s, %s, %x, %lx, %s, %li\n", debugstr_w(szProduct), |
| debugstr_w(szUserSid), dwContext, dwOptions, debugstr_w(szSource), |
| dwIndex); |
| |
| if (!szProduct) |
| return ERROR_INVALID_PARAMETER; |
| |
| if (!szSource) |
| 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"); |
| |
| if (dwContext == MSIINSTALLCONTEXT_MACHINE) |
| rc = OpenSourceKey(szProduct, &sourcekey, FALSE, TRUE); |
| else |
| rc = OpenSourceKey(szProduct, &sourcekey, TRUE, TRUE); |
| |
| if (rc != ERROR_SUCCESS) |
| return ERROR_UNKNOWN_PRODUCT; |
| |
| if (dwOptions & MSISOURCETYPE_NETWORK) |
| rc = OpenNetworkSubkey(sourcekey, &typekey, TRUE); |
| else if (dwOptions & MSISOURCETYPE_URL) |
| rc = OpenURLSubkey(sourcekey, &typekey, TRUE); |
| else |
| { |
| ERR("unknown media type: %08lx\n", dwOptions); |
| RegCloseKey(sourcekey); |
| return ERROR_FUNCTION_FAILED; |
| } |
| |
| source_struct.szIndex[0] = 0; |
| if (find_given_source(typekey, szSource, &source_struct)==ERROR_SUCCESS) |
| { |
| DWORD current_index = atoiW(source_struct.szIndex); |
| /* found the source */ |
| if (dwIndex > 0 && current_index != dwIndex) |
| FIXME("Need to reorder the sources!\n"); |
| } |
| else |
| { |
| DWORD current_index = 0; |
| static const WCHAR fmt[] = {'%','i',0}; |
| DWORD size = lstrlenW(szSource)*sizeof(WCHAR); |
| |
| if (source_struct.szIndex[0]) |
| current_index = atoiW(source_struct.szIndex); |
| /* new source */ |
| if (dwIndex > 0 && dwIndex < current_index) |
| FIXME("Need to reorder the sources!\n"); |
| |
| current_index ++; |
| sprintfW(source_struct.szIndex,fmt,current_index); |
| rc = RegSetValueExW(typekey, source_struct.szIndex, 0, REG_EXPAND_SZ, |
| (LPBYTE)szSource, size); |
| } |
| |
| 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 %lx %li %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"); |
| |
| if (dwContext == MSIINSTALLCONTEXT_MACHINE) |
| rc = OpenSourceKey(szProduct, &sourcekey, FALSE, TRUE); |
| else |
| rc = OpenSourceKey(szProduct, &sourcekey, TRUE, 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; |
| } |