oleaut32: Fix differences between the size returned in sizing the
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 )
97     {
98         r = StgCreateDocfile( szDBPath, 
99               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg);
100         if( r == ERROR_SUCCESS )
101         {
102             IStorage_SetClass( stg, &CLSID_MsiDatabase );
103             r = init_string_table( stg );
104         }
105     }
106     else if( szPersist == MSIDBOPEN_TRANSACT )
107     {
108         r = StgOpenStorage( szDBPath, NULL,
109               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
110     }
111     else
112     {
113         ERR("unknown flag %p\n",szPersist);
114         return ERROR_INVALID_PARAMETER;
115     }
116
117     if( FAILED( r ) )
118     {
119         FIXME("open failed r = %08lx!\n",r);
120         return ERROR_FUNCTION_FAILED;
121     }
122
123     r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
124     if( FAILED( r ) )
125     {
126         FIXME("Failed to stat storage\n");
127         goto end;
128     }
129
130     if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
131          !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) ) 
132     {
133         ERR("storage GUID is not a MSI database GUID %s\n",
134              debugstr_guid(&stat.clsid) );
135         goto end;
136     }
137
138     db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
139                               MSI_CloseDatabase );
140     if( !db )
141     {
142         FIXME("Failed to allocate a handle\n");
143         goto end;
144     }
145
146     if( TRACE_ON( msi ) )
147         enum_stream_names( stg );
148
149     db->storage = stg;
150     db->mode = szMode;
151     list_init( &db->tables );
152     list_init( &db->transforms );
153
154     db->strings = load_string_table( stg );
155     if( !db->strings )
156         goto end;
157
158     ret = ERROR_SUCCESS;
159
160     msiobj_addref( &db->hdr );
161     IStorage_AddRef( stg );
162     *pdb = db;
163
164 end:
165     if( db )
166         msiobj_release( &db->hdr );
167     if( stg )
168         IStorage_Release( stg );
169
170     return ret;
171 }
172
173 UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
174 {
175     MSIDATABASE *db;
176     UINT ret;
177
178     TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
179
180     ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
181     if( ret == ERROR_SUCCESS )
182     {
183         *phDB = alloc_msihandle( &db->hdr );
184         msiobj_release( &db->hdr );
185     }
186
187     return ret;
188 }
189
190 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
191 {
192     HRESULT r = ERROR_FUNCTION_FAILED;
193     LPWSTR szwDBPath = NULL, szwPersist = NULL;
194
195     TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
196
197     if( szDBPath )
198     {
199         szwDBPath = strdupAtoW( szDBPath );
200         if( !szwDBPath )
201             goto end;
202     }
203
204     if( HIWORD(szPersist) )
205     {
206         szwPersist = strdupAtoW( szPersist );
207         if( !szwPersist )
208             goto end;
209     }
210     else
211         szwPersist = (LPWSTR)(DWORD)szPersist;
212
213     r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
214
215 end:
216     if( HIWORD(szPersist) )
217         msi_free( szwPersist );
218     msi_free( szwDBPath );
219
220     return r;
221 }
222
223 UINT MSI_DatabaseImport( MSIDATABASE *db, LPCWSTR folder, LPCWSTR file )
224 {
225     FIXME("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
226
227     if( folder == NULL || file == NULL )
228         return ERROR_INVALID_PARAMETER;
229    
230     return ERROR_CALL_NOT_IMPLEMENTED;
231 }
232
233 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
234 {
235     MSIDATABASE *db;
236     UINT r;
237
238     TRACE("%lx %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
239
240     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
241     if( !db )
242         return ERROR_INVALID_HANDLE;
243     r = MSI_DatabaseImport( db, szFolder, szFilename );
244     msiobj_release( &db->hdr );
245     return r;
246 }
247
248 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
249                LPCSTR szFolder, LPCSTR szFilename )
250 {
251     LPWSTR path = NULL, file = NULL;
252     UINT r = ERROR_OUTOFMEMORY;
253
254     TRACE("%lx %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
255
256     if( szFolder )
257     {
258         path = strdupAtoW( szFolder );
259         if( !path )
260             goto end;
261     }
262
263     if( szFilename )
264     {
265         file = strdupAtoW( szFilename );
266         if( !file )
267             goto end;
268     }
269
270     r = MsiDatabaseImportW( handle, path, file );
271
272 end:
273     msi_free( path );
274     msi_free( file );
275
276     return r;
277 }
278
279 UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
280                LPCWSTR folder, LPCWSTR file )
281 {
282     FIXME("%p %s %s %s\n", db, debugstr_w(table),
283           debugstr_w(folder), debugstr_w(file) );
284
285     if( folder == NULL || file == NULL )
286         return ERROR_INVALID_PARAMETER;
287    
288     return ERROR_CALL_NOT_IMPLEMENTED;
289 }
290
291 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
292                LPCWSTR szFolder, LPCWSTR szFilename )
293 {
294     MSIDATABASE *db;
295     UINT r;
296
297     TRACE("%lx %s %s %s\n", handle, debugstr_w(szTable),
298           debugstr_w(szFolder), debugstr_w(szFilename));
299
300     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
301     if( !db )
302         return ERROR_INVALID_HANDLE;
303     r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
304     msiobj_release( &db->hdr );
305     return r;
306 }
307
308 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
309                LPCSTR szFolder, LPCSTR szFilename )
310 {
311     LPWSTR path = NULL, file = NULL, table = NULL;
312     UINT r = ERROR_OUTOFMEMORY;
313
314     TRACE("%lx %s %s %s\n", handle, debugstr_a(szTable),
315           debugstr_a(szFolder), debugstr_a(szFilename));
316
317     if( szTable )
318     {
319         table = strdupAtoW( szTable );
320         if( !table )
321             goto end;
322     }
323
324     if( szFolder )
325     {
326         path = strdupAtoW( szFolder );
327         if( !path )
328             goto end;
329     }
330
331     if( szFilename )
332     {
333         file = strdupAtoW( szFilename );
334         if( !file )
335             goto end;
336     }
337
338     r = MsiDatabaseExportW( handle, table, path, file );
339
340 end:
341     msi_free( table );
342     msi_free( path );
343     msi_free( file );
344
345     return r;
346 }
347
348 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
349 {
350     MSIDBSTATE ret = MSIDBSTATE_READ;
351     MSIDATABASE *db;
352
353     TRACE("%ld\n", handle);
354
355     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
356     if (!db)
357         return MSIDBSTATE_ERROR;
358     if (db->mode != MSIDBOPEN_READONLY )
359         ret = MSIDBSTATE_WRITE;
360     msiobj_release( &db->hdr );
361
362     return ret;
363 }