/* * Implementation of the Microsoft Installer (msi.dll) * * Copyright 2002,2003,2004,2005 Mike McCormack 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 "wine/debug.h" #include "msi.h" #include "msiquery.h" #include "msipriv.h" #include "objidl.h" #include "objbase.h" #include "initguid.h" WINE_DEFAULT_DEBUG_CHANNEL(msi); DEFINE_GUID( CLSID_MsiDatabase, 0x000c1084, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46); DEFINE_GUID( CLSID_MsiPatch, 0x000c1086, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46); /* * .MSI file format * * An .msi file is a structured storage file. * It contains a number of streams. * A stream for each table in the database. * Two streams for the string table in the database. * Any binary data in a table is a reference to a stream. */ static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg ) { MSIDATABASE *db = (MSIDATABASE *) arg; DWORD r; free_cached_tables( db ); msi_free_transforms( db ); msi_destroy_stringtable( db->strings ); r = IStorage_Release( db->storage ); if( r ) ERR("database reference count was not zero (%ld)\n", r); } UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb) { IStorage *stg = NULL; HRESULT r; MSIDATABASE *db = NULL; UINT ret = ERROR_FUNCTION_FAILED; LPCWSTR szMode; STATSTG stat; TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) ); if( !pdb ) return ERROR_INVALID_PARAMETER; szMode = szPersist; if( HIWORD( szPersist ) ) { /* UINT len = lstrlenW( szPerist ) + 1; */ FIXME("don't support persist files yet\b"); return ERROR_INVALID_PARAMETER; /* szMode = msi_alloc( len * sizeof (DWORD) ); */ } else if( szPersist == MSIDBOPEN_READONLY ) { r = StgOpenStorage( szDBPath, NULL, STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg); } else if( szPersist == MSIDBOPEN_CREATE ) { r = StgCreateDocfile( szDBPath, STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg); if( r == ERROR_SUCCESS ) { IStorage_SetClass( stg, &CLSID_MsiDatabase ); r = init_string_table( stg ); } } else if( szPersist == MSIDBOPEN_TRANSACT ) { r = StgOpenStorage( szDBPath, NULL, STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg); } else { ERR("unknown flag %p\n",szPersist); return ERROR_INVALID_PARAMETER; } if( FAILED( r ) ) { FIXME("open failed r = %08lx!\n",r); return ERROR_FUNCTION_FAILED; } r = IStorage_Stat( stg, &stat, STATFLAG_NONAME ); if( FAILED( r ) ) { FIXME("Failed to stat storage\n"); goto end; } if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) && !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) ) { ERR("storage GUID is not a MSI database GUID %s\n", debugstr_guid(&stat.clsid) ); goto end; } db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE), MSI_CloseDatabase ); if( !db ) { FIXME("Failed to allocate a handle\n"); goto end; } if( TRACE_ON( msi ) ) enum_stream_names( stg ); db->storage = stg; db->mode = szMode; list_init( &db->tables ); list_init( &db->transforms ); db->strings = load_string_table( stg ); if( !db->strings ) goto end; ret = ERROR_SUCCESS; msiobj_addref( &db->hdr ); IStorage_AddRef( stg ); *pdb = db; end: if( db ) msiobj_release( &db->hdr ); if( stg ) IStorage_Release( stg ); return ret; } UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB) { MSIDATABASE *db; UINT ret; TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB); ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db ); if( ret == ERROR_SUCCESS ) { *phDB = alloc_msihandle( &db->hdr ); msiobj_release( &db->hdr ); } return ret; } UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB) { HRESULT r = ERROR_FUNCTION_FAILED; LPWSTR szwDBPath = NULL, szwPersist = NULL; TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB); if( szDBPath ) { szwDBPath = strdupAtoW( szDBPath ); if( !szwDBPath ) goto end; } if( HIWORD(szPersist) ) { szwPersist = strdupAtoW( szPersist ); if( !szwPersist ) goto end; } else szwPersist = (LPWSTR)(DWORD_PTR)szPersist; r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB ); end: if( HIWORD(szPersist) ) msi_free( szwPersist ); msi_free( szwDBPath ); return r; } UINT MSI_DatabaseImport( MSIDATABASE *db, LPCWSTR folder, LPCWSTR file ) { FIXME("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) ); if( folder == NULL || file == NULL ) return ERROR_INVALID_PARAMETER; return ERROR_CALL_NOT_IMPLEMENTED; } UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename) { MSIDATABASE *db; UINT r; TRACE("%lx %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename)); db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE ); if( !db ) return ERROR_INVALID_HANDLE; r = MSI_DatabaseImport( db, szFolder, szFilename ); msiobj_release( &db->hdr ); return r; } UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle, LPCSTR szFolder, LPCSTR szFilename ) { LPWSTR path = NULL, file = NULL; UINT r = ERROR_OUTOFMEMORY; TRACE("%lx %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename)); if( szFolder ) { path = strdupAtoW( szFolder ); if( !path ) goto end; } if( szFilename ) { file = strdupAtoW( szFilename ); if( !file ) goto end; } r = MsiDatabaseImportW( handle, path, file ); end: msi_free( path ); msi_free( file ); return r; } UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table, LPCWSTR folder, LPCWSTR file ) { FIXME("%p %s %s %s\n", db, debugstr_w(table), debugstr_w(folder), debugstr_w(file) ); if( folder == NULL || file == NULL ) return ERROR_INVALID_PARAMETER; return ERROR_CALL_NOT_IMPLEMENTED; } UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable, LPCWSTR szFolder, LPCWSTR szFilename ) { MSIDATABASE *db; UINT r; TRACE("%lx %s %s %s\n", handle, debugstr_w(szTable), debugstr_w(szFolder), debugstr_w(szFilename)); db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE ); if( !db ) return ERROR_INVALID_HANDLE; r = MSI_DatabaseExport( db, szTable, szFolder, szFilename ); msiobj_release( &db->hdr ); return r; } UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable, LPCSTR szFolder, LPCSTR szFilename ) { LPWSTR path = NULL, file = NULL, table = NULL; UINT r = ERROR_OUTOFMEMORY; TRACE("%lx %s %s %s\n", handle, debugstr_a(szTable), debugstr_a(szFolder), debugstr_a(szFilename)); if( szTable ) { table = strdupAtoW( szTable ); if( !table ) goto end; } if( szFolder ) { path = strdupAtoW( szFolder ); if( !path ) goto end; } if( szFilename ) { file = strdupAtoW( szFilename ); if( !file ) goto end; } r = MsiDatabaseExportW( handle, table, path, file ); end: msi_free( table ); msi_free( path ); msi_free( file ); return r; } MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle ) { MSIDBSTATE ret = MSIDBSTATE_READ; MSIDATABASE *db; TRACE("%ld\n", handle); db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE ); if (!db) return MSIDBSTATE_ERROR; if (db->mode != MSIDBOPEN_READONLY ) ret = MSIDBSTATE_WRITE; msiobj_release( &db->hdr ); return ret; }