msi: Only specifically resolve the TARGETDIR directory once.
[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 "wine/unicode.h"
32 #include "msi.h"
33 #include "msiquery.h"
34 #include "msipriv.h"
35 #include "objidl.h"
36 #include "objbase.h"
37
38 #include "initguid.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(msi);
41
42 DEFINE_GUID( CLSID_MsiDatabase, 0x000c1084, 0x0000, 0x0000,
43              0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
44 DEFINE_GUID( CLSID_MsiPatch, 0x000c1086, 0x0000, 0x0000,
45              0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
46
47 /*
48  *  .MSI  file format
49  *
50  *  An .msi file is a structured storage file.
51  *  It contains a number of streams.
52  *  A stream for each table in the database.
53  *  Two streams for the string table in the database.
54  *  Any binary data in a table is a reference to a stream.
55  */
56
57 static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
58 {
59     MSIDATABASE *db = (MSIDATABASE *) arg;
60     DWORD r;
61
62     msi_free(db->path);
63     free_cached_tables( db );
64     msi_free_transforms( db );
65     msi_destroy_stringtable( db->strings );
66     r = IStorage_Release( db->storage );
67     if( r )
68         ERR("database reference count was not zero (%ld)\n", r);
69     if (db->deletefile)
70     {
71         DeleteFileW( db->deletefile );
72         msi_free( db->deletefile );
73     }
74 }
75
76 UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
77 {
78     IStorage *stg = NULL;
79     HRESULT r;
80     MSIDATABASE *db = NULL;
81     UINT ret = ERROR_FUNCTION_FAILED;
82     LPCWSTR szMode, save_path;
83     STATSTG stat;
84     BOOL created = FALSE;
85     WCHAR path[MAX_PATH];
86
87     static const WCHAR backslash[] = {'\\',0};
88
89     TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
90
91     if( !pdb )
92         return ERROR_INVALID_PARAMETER;
93
94     save_path = szDBPath;
95     szMode = szPersist;
96     if( HIWORD( szPersist ) )
97     {
98         if (!CopyFileW( szDBPath, szPersist, FALSE ))
99             return ERROR_OPEN_FAILED;
100
101         szDBPath = szPersist;
102         szPersist = MSIDBOPEN_TRANSACT;
103         created = TRUE;
104     }
105
106     if( szPersist == MSIDBOPEN_READONLY )
107     {
108         r = StgOpenStorage( szDBPath, NULL,
109               STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
110     }
111     else if( szPersist == MSIDBOPEN_CREATE || szPersist == MSIDBOPEN_CREATEDIRECT )
112     {
113         /* FIXME: MSIDBOPEN_CREATE should case STGM_TRANSACTED flag to be
114          * used here: */
115         r = StgCreateDocfile( szDBPath,
116               STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg);
117         if( r == ERROR_SUCCESS )
118         {
119             IStorage_SetClass( stg, &CLSID_MsiDatabase );
120             r = init_string_table( stg );
121         }
122         created = TRUE;
123     }
124     else if( szPersist == MSIDBOPEN_TRANSACT )
125     {
126         /* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be
127          * used here: */
128         r = StgOpenStorage( szDBPath, NULL,
129               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
130     }
131     else if( szPersist == MSIDBOPEN_DIRECT )
132     {
133         r = StgOpenStorage( szDBPath, NULL,
134               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
135     }
136     else
137     {
138         ERR("unknown flag %p\n",szPersist);
139         return ERROR_INVALID_PARAMETER;
140     }
141
142     if( FAILED( r ) )
143     {
144         FIXME("open failed r = %08lx!\n",r);
145         return ERROR_FUNCTION_FAILED;
146     }
147
148     r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
149     if( FAILED( r ) )
150     {
151         FIXME("Failed to stat storage\n");
152         goto end;
153     }
154
155     if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
156          !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) ) 
157     {
158         ERR("storage GUID is not a MSI database GUID %s\n",
159              debugstr_guid(&stat.clsid) );
160         goto end;
161     }
162
163     db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
164                               MSI_CloseDatabase );
165     if( !db )
166     {
167         FIXME("Failed to allocate a handle\n");
168         goto end;
169     }
170
171     if (!strchrW( save_path, '\\' ))
172     {
173         GetCurrentDirectoryW( MAX_PATH, path );
174         lstrcatW( path, backslash );
175         lstrcatW( path, save_path );
176     }
177     else
178         lstrcpyW( path, save_path );
179
180     db->path = strdupW( path );
181
182     if( TRACE_ON( msi ) )
183         enum_stream_names( stg );
184
185     db->storage = stg;
186     db->mode = szMode;
187     if (created)
188         db->deletefile = strdupW( szDBPath );
189     else
190         db->deletefile = NULL;
191     list_init( &db->tables );
192     list_init( &db->transforms );
193
194     db->strings = load_string_table( stg );
195     if( !db->strings )
196         goto end;
197
198     ret = ERROR_SUCCESS;
199
200     msiobj_addref( &db->hdr );
201     IStorage_AddRef( stg );
202     *pdb = db;
203
204 end:
205     if( db )
206         msiobj_release( &db->hdr );
207     if( stg )
208         IStorage_Release( stg );
209
210     return ret;
211 }
212
213 UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
214 {
215     MSIDATABASE *db;
216     UINT ret;
217
218     TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
219
220     ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
221     if( ret == ERROR_SUCCESS )
222     {
223         *phDB = alloc_msihandle( &db->hdr );
224         if (! *phDB)
225             ret = ERROR_NOT_ENOUGH_MEMORY;
226         msiobj_release( &db->hdr );
227     }
228
229     return ret;
230 }
231
232 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
233 {
234     HRESULT r = ERROR_FUNCTION_FAILED;
235     LPWSTR szwDBPath = NULL, szwPersist = NULL;
236
237     TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
238
239     if( szDBPath )
240     {
241         szwDBPath = strdupAtoW( szDBPath );
242         if( !szwDBPath )
243             goto end;
244     }
245
246     if( HIWORD(szPersist) )
247     {
248         szwPersist = strdupAtoW( szPersist );
249         if( !szwPersist )
250             goto end;
251     }
252     else
253         szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
254
255     r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
256
257 end:
258     if( HIWORD(szPersist) )
259         msi_free( szwPersist );
260     msi_free( szwDBPath );
261
262     return r;
263 }
264
265 UINT MSI_DatabaseImport( MSIDATABASE *db, LPCWSTR folder, LPCWSTR file )
266 {
267     FIXME("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
268
269     if( folder == NULL || file == NULL )
270         return ERROR_INVALID_PARAMETER;
271    
272     return ERROR_CALL_NOT_IMPLEMENTED;
273 }
274
275 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
276 {
277     MSIDATABASE *db;
278     UINT r;
279
280     TRACE("%lx %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
281
282     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
283     if( !db )
284         return ERROR_INVALID_HANDLE;
285     r = MSI_DatabaseImport( db, szFolder, szFilename );
286     msiobj_release( &db->hdr );
287     return r;
288 }
289
290 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
291                LPCSTR szFolder, LPCSTR szFilename )
292 {
293     LPWSTR path = NULL, file = NULL;
294     UINT r = ERROR_OUTOFMEMORY;
295
296     TRACE("%lx %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
297
298     if( szFolder )
299     {
300         path = strdupAtoW( szFolder );
301         if( !path )
302             goto end;
303     }
304
305     if( szFilename )
306     {
307         file = strdupAtoW( szFilename );
308         if( !file )
309             goto end;
310     }
311
312     r = MsiDatabaseImportW( handle, path, file );
313
314 end:
315     msi_free( path );
316     msi_free( file );
317
318     return r;
319 }
320
321 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
322 {
323     UINT i, count, len, r = ERROR_SUCCESS;
324     const char *sep;
325     char *buffer;
326     DWORD sz;
327
328     len = 0x100;
329     buffer = msi_alloc( len );
330     if ( !buffer )
331         return ERROR_OUTOFMEMORY;
332
333     count = MSI_RecordGetFieldCount( row );
334     for ( i=start; i<=count; i++ )
335     {
336         sz = len;
337         r = MSI_RecordGetStringA( row, i, buffer, &sz );
338         if (r == ERROR_MORE_DATA)
339         {
340             char *p = msi_realloc( buffer, sz + 1 );
341             if (!p)
342                 break;
343             len = sz + 1;
344             buffer = p;
345         }
346         sz = len;
347         r = MSI_RecordGetStringA( row, i, buffer, &sz );
348         if (r != ERROR_SUCCESS)
349             break;
350
351         if (!WriteFile( handle, buffer, sz, &sz, NULL ))
352         {
353             r = ERROR_FUNCTION_FAILED;
354             break;
355         }
356
357         sep = (i < count) ? "\t" : "\r\n";
358         if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
359         {
360             r = ERROR_FUNCTION_FAILED;
361             break;
362         }
363     }
364     msi_free( buffer );
365     return r;
366 }
367
368 static UINT msi_export_row( MSIRECORD *row, void *arg )
369 {
370     return msi_export_record( arg, row, 1 );
371 }
372
373 UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
374                LPCWSTR folder, LPCWSTR file )
375 {
376     static const WCHAR query[] = {
377         's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
378     static const WCHAR szbs[] = { '\\', 0 };
379     MSIRECORD *rec = NULL;
380     MSIQUERY *view = NULL;
381     LPWSTR filename;
382     HANDLE handle;
383     UINT len, r;
384
385     TRACE("%p %s %s %s\n", db, debugstr_w(table),
386           debugstr_w(folder), debugstr_w(file) );
387
388     if( folder == NULL || file == NULL )
389         return ERROR_INVALID_PARAMETER;
390
391     len = lstrlenW(folder) + lstrlenW(file) + 2;
392     filename = msi_alloc(len * sizeof (WCHAR));
393     if (!filename)
394         return ERROR_OUTOFMEMORY;
395
396     lstrcpyW( filename, folder );
397     lstrcatW( filename, szbs );
398     lstrcatW( filename, file );
399
400     handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
401                           NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
402     msi_free( filename );
403     if (handle == INVALID_HANDLE_VALUE)
404         return ERROR_FUNCTION_FAILED;
405
406     r = MSI_OpenQuery( db, &view, query, table );
407     if (r == ERROR_SUCCESS)
408     {
409         /* write out row 1, the column names */
410         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
411         if (r == ERROR_SUCCESS)
412         {
413             msi_export_record( handle, rec, 1 );
414             msiobj_release( &rec->hdr );
415         }
416
417         /* write out row 2, the column types */
418         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
419         if (r == ERROR_SUCCESS)
420         {
421             msi_export_record( handle, rec, 1 );
422             msiobj_release( &rec->hdr );
423         }
424
425         /* write out row 3, the table name + keys */
426         r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
427         if (r == ERROR_SUCCESS)
428         {
429             MSI_RecordSetStringW( rec, 0, table );
430             msi_export_record( handle, rec, 0 );
431             msiobj_release( &rec->hdr );
432         }
433
434         /* write out row 4 onwards, the data */
435         r = MSI_IterateRecords( view, 0, msi_export_row, handle );
436         msiobj_release( &view->hdr );
437     }
438
439     CloseHandle( handle );
440
441     return r;
442 }
443
444 /***********************************************************************
445  * MsiExportDatabaseW        [MSI.@]
446  *
447  * Writes a file containing the table data as tab separated ASCII.
448  *
449  * The format is as follows:
450  *
451  * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
452  * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
453  * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
454  *
455  * Followed by the data, starting at row 1 with one row per line
456  *
457  * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
458  */
459 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
460                LPCWSTR szFolder, LPCWSTR szFilename )
461 {
462     MSIDATABASE *db;
463     UINT r;
464
465     TRACE("%lx %s %s %s\n", handle, debugstr_w(szTable),
466           debugstr_w(szFolder), debugstr_w(szFilename));
467
468     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
469     if( !db )
470         return ERROR_INVALID_HANDLE;
471     r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
472     msiobj_release( &db->hdr );
473     return r;
474 }
475
476 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
477                LPCSTR szFolder, LPCSTR szFilename )
478 {
479     LPWSTR path = NULL, file = NULL, table = NULL;
480     UINT r = ERROR_OUTOFMEMORY;
481
482     TRACE("%lx %s %s %s\n", handle, debugstr_a(szTable),
483           debugstr_a(szFolder), debugstr_a(szFilename));
484
485     if( szTable )
486     {
487         table = strdupAtoW( szTable );
488         if( !table )
489             goto end;
490     }
491
492     if( szFolder )
493     {
494         path = strdupAtoW( szFolder );
495         if( !path )
496             goto end;
497     }
498
499     if( szFilename )
500     {
501         file = strdupAtoW( szFilename );
502         if( !file )
503             goto end;
504     }
505
506     r = MsiDatabaseExportW( handle, table, path, file );
507
508 end:
509     msi_free( table );
510     msi_free( path );
511     msi_free( file );
512
513     return r;
514 }
515
516 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
517 {
518     MSIDBSTATE ret = MSIDBSTATE_READ;
519     MSIDATABASE *db;
520
521     TRACE("%ld\n", handle);
522
523     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
524     if (!db)
525         return MSIDBSTATE_ERROR;
526     if (db->mode != MSIDBOPEN_READONLY )
527         ret = MSIDBSTATE_WRITE;
528     msiobj_release( &db->hdr );
529
530     return ret;
531 }