msi: Add a stub implementation of the VolumeCostList control.
[wine] / dlls / msi / database.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002,2003,2004,2005 Mike McCormack for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winreg.h"
29 #include "winnls.h"
30 #include "wine/debug.h"
31 #include "msi.h"
32 #include "msiquery.h"
33 #include "msipriv.h"
34 #include "objidl.h"
35 #include "objbase.h"
36
37 #include "initguid.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(msi);
40
41 DEFINE_GUID( CLSID_MsiDatabase, 0x000c1084, 0x0000, 0x0000,
42              0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
43 DEFINE_GUID( CLSID_MsiPatch, 0x000c1086, 0x0000, 0x0000,
44              0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
45
46 /*
47  *  .MSI  file format
48  *
49  *  An .msi file is a structured storage file.
50  *  It contains a number of streams.
51  *  A stream for each table in the database.
52  *  Two streams for the string table in the database.
53  *  Any binary data in a table is a reference to a stream.
54  */
55
56 static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
57 {
58     MSIDATABASE *db = (MSIDATABASE *) arg;
59     DWORD r;
60
61     free_cached_tables( db );
62     msi_free_transforms( db );
63     msi_destroy_stringtable( db->strings );
64     r = IStorage_Release( db->storage );
65     if( r )
66         ERR("database reference count was not zero (%ld)\n", r);
67 }
68
69 UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
70 {
71     IStorage *stg = NULL;
72     HRESULT r;
73     MSIDATABASE *db = NULL;
74     UINT ret = ERROR_FUNCTION_FAILED;
75     LPCWSTR szMode;
76     STATSTG stat;
77
78     TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
79
80     if( !pdb )
81         return ERROR_INVALID_PARAMETER;
82
83     szMode = szPersist;
84     if( HIWORD( szPersist ) )
85     {
86         /* UINT len = lstrlenW( szPerist ) + 1; */
87         FIXME("don't support persist files yet\b");
88         return ERROR_INVALID_PARAMETER;
89         /* szMode = msi_alloc( len * sizeof (DWORD) ); */
90     }
91     else if( szPersist == MSIDBOPEN_READONLY )
92     {
93         r = StgOpenStorage( szDBPath, NULL,
94               STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
95     }
96     else if( szPersist == MSIDBOPEN_CREATE || szPersist == MSIDBOPEN_CREATEDIRECT )
97     {
98         /* FIXME: MSIDBOPEN_CREATE should case STGM_TRANSACTED flag to be
99          * used here: */
100         r = StgCreateDocfile( szDBPath, 
101               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg);
102         if( r == ERROR_SUCCESS )
103         {
104             IStorage_SetClass( stg, &CLSID_MsiDatabase );
105             r = init_string_table( stg );
106         }
107     }
108     else if( szPersist == MSIDBOPEN_TRANSACT )
109     {
110         /* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be
111          * used here: */
112         r = StgOpenStorage( szDBPath, NULL,
113               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
114     }
115     else if( szPersist == MSIDBOPEN_DIRECT )
116     {
117         r = StgOpenStorage( szDBPath, NULL,
118               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
119     }
120     else
121     {
122         ERR("unknown flag %p\n",szPersist);
123         return ERROR_INVALID_PARAMETER;
124     }
125
126     if( FAILED( r ) )
127     {
128         FIXME("open failed r = %08lx!\n",r);
129         return ERROR_FUNCTION_FAILED;
130     }
131
132     r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
133     if( FAILED( r ) )
134     {
135         FIXME("Failed to stat storage\n");
136         goto end;
137     }
138
139     if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
140          !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) ) 
141     {
142         ERR("storage GUID is not a MSI database GUID %s\n",
143              debugstr_guid(&stat.clsid) );
144         goto end;
145     }
146
147     db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
148                               MSI_CloseDatabase );
149     if( !db )
150     {
151         FIXME("Failed to allocate a handle\n");
152         goto end;
153     }
154
155     if( TRACE_ON( msi ) )
156         enum_stream_names( stg );
157
158     db->storage = stg;
159     db->mode = szMode;
160     list_init( &db->tables );
161     list_init( &db->transforms );
162
163     db->strings = load_string_table( stg );
164     if( !db->strings )
165         goto end;
166
167     ret = ERROR_SUCCESS;
168
169     msiobj_addref( &db->hdr );
170     IStorage_AddRef( stg );
171     *pdb = db;
172
173 end:
174     if( db )
175         msiobj_release( &db->hdr );
176     if( stg )
177         IStorage_Release( stg );
178
179     return ret;
180 }
181
182 UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
183 {
184     MSIDATABASE *db;
185     UINT ret;
186
187     TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
188
189     ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
190     if( ret == ERROR_SUCCESS )
191     {
192         *phDB = alloc_msihandle( &db->hdr );
193         msiobj_release( &db->hdr );
194     }
195
196     return ret;
197 }
198
199 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
200 {
201     HRESULT r = ERROR_FUNCTION_FAILED;
202     LPWSTR szwDBPath = NULL, szwPersist = NULL;
203
204     TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
205
206     if( szDBPath )
207     {
208         szwDBPath = strdupAtoW( szDBPath );
209         if( !szwDBPath )
210             goto end;
211     }
212
213     if( HIWORD(szPersist) )
214     {
215         szwPersist = strdupAtoW( szPersist );
216         if( !szwPersist )
217             goto end;
218     }
219     else
220         szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
221
222     r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
223
224 end:
225     if( HIWORD(szPersist) )
226         msi_free( szwPersist );
227     msi_free( szwDBPath );
228
229     return r;
230 }
231
232 UINT MSI_DatabaseImport( MSIDATABASE *db, LPCWSTR folder, LPCWSTR file )
233 {
234     FIXME("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
235
236     if( folder == NULL || file == NULL )
237         return ERROR_INVALID_PARAMETER;
238    
239     return ERROR_CALL_NOT_IMPLEMENTED;
240 }
241
242 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
243 {
244     MSIDATABASE *db;
245     UINT r;
246
247     TRACE("%lx %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
248
249     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
250     if( !db )
251         return ERROR_INVALID_HANDLE;
252     r = MSI_DatabaseImport( db, szFolder, szFilename );
253     msiobj_release( &db->hdr );
254     return r;
255 }
256
257 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
258                LPCSTR szFolder, LPCSTR szFilename )
259 {
260     LPWSTR path = NULL, file = NULL;
261     UINT r = ERROR_OUTOFMEMORY;
262
263     TRACE("%lx %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
264
265     if( szFolder )
266     {
267         path = strdupAtoW( szFolder );
268         if( !path )
269             goto end;
270     }
271
272     if( szFilename )
273     {
274         file = strdupAtoW( szFilename );
275         if( !file )
276             goto end;
277     }
278
279     r = MsiDatabaseImportW( handle, path, file );
280
281 end:
282     msi_free( path );
283     msi_free( file );
284
285     return r;
286 }
287
288 UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
289                LPCWSTR folder, LPCWSTR file )
290 {
291     FIXME("%p %s %s %s\n", db, debugstr_w(table),
292           debugstr_w(folder), debugstr_w(file) );
293
294     if( folder == NULL || file == NULL )
295         return ERROR_INVALID_PARAMETER;
296    
297     return ERROR_CALL_NOT_IMPLEMENTED;
298 }
299
300 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
301                LPCWSTR szFolder, LPCWSTR szFilename )
302 {
303     MSIDATABASE *db;
304     UINT r;
305
306     TRACE("%lx %s %s %s\n", handle, debugstr_w(szTable),
307           debugstr_w(szFolder), debugstr_w(szFilename));
308
309     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
310     if( !db )
311         return ERROR_INVALID_HANDLE;
312     r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
313     msiobj_release( &db->hdr );
314     return r;
315 }
316
317 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
318                LPCSTR szFolder, LPCSTR szFilename )
319 {
320     LPWSTR path = NULL, file = NULL, table = NULL;
321     UINT r = ERROR_OUTOFMEMORY;
322
323     TRACE("%lx %s %s %s\n", handle, debugstr_a(szTable),
324           debugstr_a(szFolder), debugstr_a(szFilename));
325
326     if( szTable )
327     {
328         table = strdupAtoW( szTable );
329         if( !table )
330             goto end;
331     }
332
333     if( szFolder )
334     {
335         path = strdupAtoW( szFolder );
336         if( !path )
337             goto end;
338     }
339
340     if( szFilename )
341     {
342         file = strdupAtoW( szFilename );
343         if( !file )
344             goto end;
345     }
346
347     r = MsiDatabaseExportW( handle, table, path, file );
348
349 end:
350     msi_free( table );
351     msi_free( path );
352     msi_free( file );
353
354     return r;
355 }
356
357 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
358 {
359     MSIDBSTATE ret = MSIDBSTATE_READ;
360     MSIDATABASE *db;
361
362     TRACE("%ld\n", handle);
363
364     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
365     if (!db)
366         return MSIDBSTATE_ERROR;
367     if (db->mode != MSIDBOPEN_READONLY )
368         ret = MSIDBSTATE_WRITE;
369     msiobj_release( &db->hdr );
370
371     return ret;
372 }