/* * Implementation of the Microsoft Installer (msi.dll) * * Copyright 2004 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 */ #define NONAMELESSUNION #include #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 "objidl.h" #include "wincrypt.h" #include "winuser.h" #include "shlobj.h" #include "wine/unicode.h" #include "objbase.h" WINE_DEFAULT_DEBUG_CHANNEL(msi); /* * The MSVC headers define the MSIDBOPEN_* macros cast to LPCTSTR, * which is a problem because LPCTSTR isn't defined when compiling wine. * To work around this problem, we need to define LPCTSTR as LPCWSTR here, * and make sure to only use it in W functions. */ #define LPCTSTR LPCWSTR void MSI_FreePackage( VOID *arg); typedef struct tagMSIPACKAGE { MSIHANDLE db; } MSIPACKAGE; void MSI_FreePackage( VOID *arg) { MSIPACKAGE *package= arg; MsiCloseHandle(package->db); } UINT WINAPI MsiOpenPackageA(LPCSTR szPackage, MSIHANDLE *phPackage) { LPWSTR szwPack = NULL; UINT len, ret; TRACE("%s %p\n",debugstr_a(szPackage), phPackage); if( szPackage ) { len = MultiByteToWideChar( CP_ACP, 0, szPackage, -1, NULL, 0 ); szwPack = HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) ); if( szwPack ) MultiByteToWideChar( CP_ACP, 0, szPackage, -1, szwPack, len ); } ret = MsiOpenPackageW( szwPack, phPackage ); if( szwPack ) HeapFree( GetProcessHeap(), 0, szwPack ); return ret; } static const void clone_properties(MSIHANDLE db) { MSIHANDLE view; UINT rc; static const CHAR CreateSql[] = "CREATE TABLE `_Property` ( `_Property` " "CHAR(56) NOT NULL, `Value` CHAR(98) NOT NULL PRIMARY KEY `_Property`)"; static const CHAR Query[] = "SELECT * from Property"; static const CHAR Insert[] = "INSERT into `_Property` (`_Property`,`Value`) VALUES (?)"; /* create the temporary properties table */ MsiDatabaseOpenViewA(db, CreateSql, &view); MsiViewExecute(view,0); MsiViewClose(view); MsiCloseHandle(view); /* clone the existing properties */ MsiDatabaseOpenViewA(db, Query, &view); MsiViewExecute(view, 0); while (1) { MSIHANDLE row; MSIHANDLE view2; rc = MsiViewFetch(view,&row); if (rc != ERROR_SUCCESS) break; MsiDatabaseOpenViewA(db,Insert,&view2); MsiViewExecute(view2,row); MsiViewClose(view2); MsiCloseHandle(view2); MsiCloseHandle(row); } MsiViewClose(view); MsiCloseHandle(view); } /* * There are a whole slew of these we need to set * * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/properties.asp */ static VOID set_installer_properties(MSIHANDLE hPackage) { WCHAR pth[MAX_PATH]; static const WCHAR cszbs[]={'\\',0}; static const WCHAR CFF[] = {'C','o','m','m','o','n','F','i','l','e','s','F','o','l','d','e','r',0}; static const WCHAR PFF[] = {'P','r','o','g','r','a','m','F','i','l','e','s','F','o','l','d','e','r',0}; static const WCHAR CADF[] = {'C','o','m','m','o','n','A','p','p','D','a','t','a','F','o','l','d','e','r',0}; static const WCHAR ATF[] = {'A','d','m','i','n','T','o','o','l','s','F','o','l','d','e','r',0}; static const WCHAR ADF[] = {'A','p','p','D','a','t','a','F','o','l','d','e','r',0}; static const WCHAR SF[] = {'S','y','s','t','e','m','F','o','l','d','e','r',0}; static const WCHAR LADF[] = {'L','o','c','a','l','A','p','p','D','a','t','a','F','o','l','d','e','r',0}; static const WCHAR MPF[] = {'M','y','P','i','c','t','u','r','e','s','F','o','l','d','e','r',0}; static const WCHAR PF[] = {'P','e','r','s','o','n','a','l','F','o','l','d','e','r',0}; static const WCHAR WF[] = {'W','i','n','d','o','w','s','F','o','l','d','e','r',0}; static const WCHAR TF[]= {'T','e','m','p','F','o','l','d','e','r',0}; /* Not yet set ... but needed by iTunes * DesktopFolder FavoritesFolder FontsFolder PrimaryVolumePath ProgramFiles64Folder ProgramMenuFolder SendToFolder StartMenuFolder StartupFolder System16Folder System64Folder TemplateFolder */ /* asked for by iTunes ... but are they installer set? * * GlobalAssemblyCache */ /* * Other things i notice set * ScreenY ScreenX SystemLanguageID ComputerName UserLanguageID LogonUser VirtualMemory PhysicalMemory Intel ShellAdvSupport ServicePackLevel WindowsBuild Version9x Version95 VersionNT AdminUser DefaultUIFont VersionMsi VersionDatabase PackagecodeChanging ProductState CaptionHeight BorderTop BorderSide TextHeight ColorBits RedirectedDllSupport Time Date Privilaged DATABASE OriginalDatabase UILevel */ SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES_COMMON,NULL,0,pth); strcatW(pth,cszbs); MsiSetPropertyW(hPackage, CFF, pth); SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES,NULL,0,pth); strcatW(pth,cszbs); MsiSetPropertyW(hPackage, PFF, pth); SHGetFolderPathW(NULL,CSIDL_COMMON_APPDATA,NULL,0,pth); strcatW(pth,cszbs); MsiSetPropertyW(hPackage, CADF, pth); SHGetFolderPathW(NULL,CSIDL_ADMINTOOLS,NULL,0,pth); strcatW(pth,cszbs); MsiSetPropertyW(hPackage, ATF, pth); SHGetFolderPathW(NULL,CSIDL_APPDATA,NULL,0,pth); strcatW(pth,cszbs); MsiSetPropertyW(hPackage, ADF, pth); SHGetFolderPathW(NULL,CSIDL_SYSTEM,NULL,0,pth); strcatW(pth,cszbs); MsiSetPropertyW(hPackage, SF, pth); SHGetFolderPathW(NULL,CSIDL_LOCAL_APPDATA,NULL,0,pth); strcatW(pth,cszbs); MsiSetPropertyW(hPackage, LADF, pth); SHGetFolderPathW(NULL,CSIDL_MYPICTURES,NULL,0,pth); strcatW(pth,cszbs); MsiSetPropertyW(hPackage, MPF, pth); SHGetFolderPathW(NULL,CSIDL_PERSONAL,NULL,0,pth); strcatW(pth,cszbs); MsiSetPropertyW(hPackage, PF, pth); SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth); strcatW(pth,cszbs); MsiSetPropertyW(hPackage, WF, pth); GetTempPathW(MAX_PATH,pth); MsiSetPropertyW(hPackage, TF, pth); } UINT WINAPI MsiOpenPackageW(LPCWSTR szPackage, MSIHANDLE *phPackage) { UINT rc; MSIHANDLE handle; MSIHANDLE db; MSIPACKAGE *package; static const WCHAR OriginalDatabase[] = {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0}; static const WCHAR Database[] = {'D','A','T','A','B','A','S','E',0}; TRACE("%s %p\n",debugstr_w(szPackage), phPackage); rc = MsiOpenDatabaseW(szPackage, MSIDBOPEN_READONLY, &db); if (rc != ERROR_SUCCESS) return ERROR_FUNCTION_FAILED; handle = alloc_msihandle(MSIHANDLETYPE_PACKAGE, sizeof (MSIPACKAGE), MSI_FreePackage, (void**)&package); if (!handle) { MsiCloseHandle(db); return ERROR_FUNCTION_FAILED; } package->db = db; /* ok here is where we do a slew of things to the database to * prep for all that is to come as a package */ clone_properties(db); set_installer_properties(handle); MsiSetPropertyW(handle, OriginalDatabase, szPackage); MsiSetPropertyW(handle, Database, szPackage); *phPackage = handle; return ERROR_SUCCESS; } UINT WINAPI MsiOpenPackageExA(LPCSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage) { FIXME("%s 0x%08lx %p\n",debugstr_a(szPackage), dwOptions, phPackage); return ERROR_CALL_NOT_IMPLEMENTED; } UINT WINAPI MsiOpenPackageExW(LPCWSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage) { FIXME("%s 0x%08lx %p\n",debugstr_w(szPackage), dwOptions, phPackage); return ERROR_CALL_NOT_IMPLEMENTED; } MSIHANDLE WINAPI MsiGetActiveDatabase(MSIHANDLE hInstall) { MSIPACKAGE *package; TRACE("(%ld)\n",hInstall); package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE); if( !package) return ERROR_INVALID_HANDLE; msihandle_addref(package->db); return package->db; } INT WINAPI MsiProcessMessage( MSIHANDLE hInstall, INSTALLMESSAGE eMessageType, MSIHANDLE hRecord) { FIXME("STUB: \n"); return ERROR_SUCCESS; } /* property code */ UINT WINAPI MsiSetPropertyA( MSIHANDLE hInstall, LPCSTR szName, LPCSTR szValue) { LPWSTR szwName = NULL, szwValue = NULL; UINT hr = ERROR_INSTALL_FAILURE; UINT len; if (0 == hInstall) { return ERROR_INVALID_HANDLE; } if (NULL == szName) { return ERROR_INVALID_PARAMETER; } if (NULL == szValue) { return ERROR_INVALID_PARAMETER; } len = MultiByteToWideChar( CP_ACP, 0, szName, -1, NULL, 0 ); szwName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); if( !szwName ) goto end; MultiByteToWideChar( CP_ACP, 0, szName, -1, szwName, len ); len = MultiByteToWideChar( CP_ACP, 0, szValue, -1, NULL, 0 ); szwValue = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); if( !szwValue) goto end; MultiByteToWideChar( CP_ACP, 0, szValue , -1, szwValue, len ); hr = MsiSetPropertyW( hInstall, szwName, szwValue); end: if( szwName ) HeapFree( GetProcessHeap(), 0, szwName ); if( szwValue ) HeapFree( GetProcessHeap(), 0, szwValue ); return hr; } UINT WINAPI MsiSetPropertyW( MSIHANDLE hInstall, LPCWSTR szName, LPCWSTR szValue) { MSIPACKAGE *package; MSIHANDLE view,row; UINT rc; DWORD sz = 0; static const CHAR Insert[]= "INSERT into `_Property` (`_Property`,`Value`) VALUES (?)"; TRACE("Setting property (%s %s)\n",debugstr_w(szName), debugstr_w(szValue)); if (!hInstall) return ERROR_INVALID_HANDLE; if (MsiGetPropertyW(hInstall,szName,0,&sz)==ERROR_MORE_DATA) { FIXME("Cannot set exising properties! FIXME MIKE!\n"); return ERROR_FUNCTION_FAILED; } package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE); if( !package) return ERROR_INVALID_HANDLE; rc = MsiDatabaseOpenViewA(package->db,Insert,&view); if (rc!= ERROR_SUCCESS) return rc; row = MsiCreateRecord(2); MsiRecordSetStringW(row,1,szName); MsiRecordSetStringW(row,2,szValue); rc = MsiViewExecute(view,row); MsiCloseHandle(row); MsiViewClose(view); MsiCloseHandle(view); return rc; } UINT WINAPI MsiGetPropertyA(MSIHANDLE hInstall, LPCSTR szName, LPSTR szValueBuf, DWORD* pchValueBuf) { LPWSTR szwName = NULL, szwValueBuf = NULL; UINT hr = ERROR_INSTALL_FAILURE; if (0 == hInstall) { return ERROR_INVALID_HANDLE; } if (NULL == szName) { return ERROR_INVALID_PARAMETER; } TRACE("%lu %s %lu\n", hInstall, debugstr_a(szName), *pchValueBuf); if (NULL != szValueBuf && NULL == pchValueBuf) { return ERROR_INVALID_PARAMETER; } if( szName ) { UINT len = MultiByteToWideChar( CP_ACP, 0, szName, -1, NULL, 0 ); szwName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); if( !szwName ) goto end; MultiByteToWideChar( CP_ACP, 0, szName, -1, szwName, len ); } else { return ERROR_INVALID_PARAMETER; } if( szValueBuf ) { szwValueBuf = HeapAlloc( GetProcessHeap(), 0, (*pchValueBuf) * sizeof(WCHAR) ); if( !szwValueBuf ) goto end; } hr = MsiGetPropertyW( hInstall, szwName, szwValueBuf, pchValueBuf ); if( *pchValueBuf > 0 ) { WideCharToMultiByte(CP_ACP, 0, szwValueBuf, -1, szValueBuf, *pchValueBuf, NULL, NULL); } end: if( szwName ) HeapFree( GetProcessHeap(), 0, szwName ); if( szwValueBuf ) HeapFree( GetProcessHeap(), 0, szwValueBuf ); return hr; } UINT WINAPI MsiGetPropertyW(MSIHANDLE hInstall, LPCWSTR szName, LPWSTR szValueBuf, DWORD* pchValueBuf) { MSIHANDLE view,row; UINT rc; WCHAR Query[1024]= {'s','e','l','e','c','t',' ','V','a','l','u','e',' ','f','r','o','m',' ' ,'_','P','r','o','p','e','r','t','y',' ','w','h','e','r','e',' ' ,'_','P','r','o','p','e','r','t','y','=','`',0}; static const WCHAR szEnd[]={'`',0}; MSIPACKAGE *package; if (0 == hInstall) { return ERROR_INVALID_HANDLE; } if (NULL == szName) { return ERROR_INVALID_PARAMETER; } package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE); if( !package) return ERROR_INVALID_HANDLE; strcatW(Query,szName); strcatW(Query,szEnd); rc = MsiDatabaseOpenViewW(package->db, Query, &view); if (rc == ERROR_SUCCESS) { DWORD sz; WCHAR value[0x100]; rc = MsiViewExecute(view, 0); if (rc != ERROR_SUCCESS) { MsiViewClose(view); MsiCloseHandle(view); return rc; } rc = MsiViewFetch(view,&row); if (rc == ERROR_SUCCESS) { sz=0x100; rc = MsiRecordGetStringW(row,1,value,&sz); strncpyW(szValueBuf,value,min(sz+1,*pchValueBuf)); *pchValueBuf = sz+1; MsiCloseHandle(row); } MsiViewClose(view); MsiCloseHandle(view); } if (rc == ERROR_SUCCESS) TRACE("returning %s for property %s\n", debugstr_w(szValueBuf), debugstr_w(szName)); else { *pchValueBuf = 0; TRACE("property not found\n"); } return rc; }