msi: Create a function to copy record fields, use it to order INSERT fields correctly.
[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         if (! *phDB)
194             ret = ERROR_NOT_ENOUGH_MEMORY;
195         msiobj_release( &db->hdr );
196     }
197
198     return ret;
199 }
200
201 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
202 {
203     HRESULT r = ERROR_FUNCTION_FAILED;
204     LPWSTR szwDBPath = NULL, szwPersist = NULL;
205
206     TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
207
208     if( szDBPath )
209     {
210         szwDBPath = strdupAtoW( szDBPath );
211         if( !szwDBPath )
212             goto end;
213     }
214
215     if( HIWORD(szPersist) )
216     {
217         szwPersist = strdupAtoW( szPersist );
218         if( !szwPersist )
219             goto end;
220     }
221     else
222         szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
223
224     r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
225
226 end:
227     if( HIWORD(szPersist) )
228         msi_free( szwPersist );
229     msi_free( szwDBPath );
230
231     return r;
232 }
233
234 UINT MSI_DatabaseImport( MSIDATABASE *db, LPCWSTR folder, LPCWSTR file )
235 {
236     FIXME("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
237
238     if( folder == NULL || file == NULL )
239         return ERROR_INVALID_PARAMETER;
240    
241     return ERROR_CALL_NOT_IMPLEMENTED;
242 }
243
244 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
245 {
246     MSIDATABASE *db;
247     UINT r;
248
249     TRACE("%lx %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
250
251     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
252     if( !db )
253         return ERROR_INVALID_HANDLE;
254     r = MSI_DatabaseImport( db, szFolder, szFilename );
255     msiobj_release( &db->hdr );
256     return r;
257 }
258
259 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
260                LPCSTR szFolder, LPCSTR szFilename )
261 {
262     LPWSTR path = NULL, file = NULL;
263     UINT r = ERROR_OUTOFMEMORY;
264
265     TRACE("%lx %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
266
267     if( szFolder )
268     {
269         path = strdupAtoW( szFolder );
270         if( !path )
271             goto end;
272     }
273
274     if( szFilename )
275     {
276         file = strdupAtoW( szFilename );
277         if( !file )
278             goto end;
279     }
280
281     r = MsiDatabaseImportW( handle, path, file );
282
283 end:
284     msi_free( path );
285     msi_free( file );
286
287     return r;
288 }
289
290 UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
291                LPCWSTR folder, LPCWSTR file )
292 {
293     FIXME("%p %s %s %s\n", db, debugstr_w(table),
294           debugstr_w(folder), debugstr_w(file) );
295
296     if( folder == NULL || file == NULL )
297         return ERROR_INVALID_PARAMETER;
298    
299     return ERROR_CALL_NOT_IMPLEMENTED;
300 }
301
302 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
303                LPCWSTR szFolder, LPCWSTR szFilename )
304 {
305     MSIDATABASE *db;
306     UINT r;
307
308     TRACE("%lx %s %s %s\n", handle, debugstr_w(szTable),
309           debugstr_w(szFolder), debugstr_w(szFilename));
310
311     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
312     if( !db )
313         return ERROR_INVALID_HANDLE;
314     r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
315     msiobj_release( &db->hdr );
316     return r;
317 }
318
319 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
320                LPCSTR szFolder, LPCSTR szFilename )
321 {
322     LPWSTR path = NULL, file = NULL, table = NULL;
323     UINT r = ERROR_OUTOFMEMORY;
324
325     TRACE("%lx %s %s %s\n", handle, debugstr_a(szTable),
326           debugstr_a(szFolder), debugstr_a(szFilename));
327
328     if( szTable )
329     {
330         table = strdupAtoW( szTable );
331         if( !table )
332             goto end;
333     }
334
335     if( szFolder )
336     {
337         path = strdupAtoW( szFolder );
338         if( !path )
339             goto end;
340     }
341
342     if( szFilename )
343     {
344         file = strdupAtoW( szFilename );
345         if( !file )
346             goto end;
347     }
348
349     r = MsiDatabaseExportW( handle, table, path, file );
350
351 end:
352     msi_free( table );
353     msi_free( path );
354     msi_free( file );
355
356     return r;
357 }
358
359 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
360 {
361     MSIDBSTATE ret = MSIDBSTATE_READ;
362     MSIDATABASE *db;
363
364     TRACE("%ld\n", handle);
365
366     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
367     if (!db)
368         return MSIDBSTATE_ERROR;
369     if (db->mode != MSIDBOPEN_READONLY )
370         ret = MSIDBSTATE_WRITE;
371     msiobj_release( &db->hdr );
372
373     return ret;
374 }