gdi32: Fix DefaultGuiFont name string for Korean Charset.
[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 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
291 {
292     UINT i, count, len, r = ERROR_SUCCESS;
293     const char *sep;
294     char *buffer;
295     DWORD sz;
296
297     len = 0x100;
298     buffer = msi_alloc( len );
299     if ( !buffer )
300         return ERROR_OUTOFMEMORY;
301
302     count = MSI_RecordGetFieldCount( row );
303     for ( i=start; i<=count; i++ )
304     {
305         sz = len;
306         r = MSI_RecordGetStringA( row, i, buffer, &sz );
307         if (r == ERROR_MORE_DATA)
308         {
309             char *p = msi_realloc( buffer, sz + 1 );
310             if (!p)
311                 break;
312             len = sz + 1;
313             buffer = p;
314         }
315         sz = len;
316         r = MSI_RecordGetStringA( row, i, buffer, &sz );
317         if (r != ERROR_SUCCESS)
318             break;
319
320         if (!WriteFile( handle, buffer, sz, &sz, NULL ))
321         {
322             r = ERROR_FUNCTION_FAILED;
323             break;
324         }
325
326         sep = (i < count) ? "\t" : "\r\n";
327         if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
328         {
329             r = ERROR_FUNCTION_FAILED;
330             break;
331         }
332     }
333     msi_free( buffer );
334     return r;
335 }
336
337 static UINT msi_export_row( MSIRECORD *row, void *arg )
338 {
339     return msi_export_record( arg, row, 1 );
340 }
341
342 UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
343                LPCWSTR folder, LPCWSTR file )
344 {
345     static const WCHAR query[] = {
346         's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
347     static const WCHAR szbs[] = { '\\', 0 };
348     MSIRECORD *rec = NULL;
349     MSIQUERY *view = NULL;
350     LPWSTR filename;
351     HANDLE handle;
352     UINT len, r;
353
354     TRACE("%p %s %s %s\n", db, debugstr_w(table),
355           debugstr_w(folder), debugstr_w(file) );
356
357     if( folder == NULL || file == NULL )
358         return ERROR_INVALID_PARAMETER;
359
360     len = lstrlenW(folder) + lstrlenW(file) + 2;
361     filename = msi_alloc(len * sizeof (WCHAR));
362     if (!filename)
363         return ERROR_OUTOFMEMORY;
364
365     lstrcpyW( filename, folder );
366     lstrcatW( filename, szbs );
367     lstrcatW( filename, file );
368
369     handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
370                           NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
371     msi_free( filename );
372     if (handle == INVALID_HANDLE_VALUE)
373         return ERROR_FUNCTION_FAILED;
374
375     r = MSI_OpenQuery( db, &view, query, table );
376     if (r == ERROR_SUCCESS)
377     {
378         /* write out row 1, the column names */
379         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
380         if (r == ERROR_SUCCESS)
381         {
382             msi_export_record( handle, rec, 1 );
383             msiobj_release( &rec->hdr );
384         }
385
386         /* write out row 2, the column types */
387         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
388         if (r == ERROR_SUCCESS)
389         {
390             msi_export_record( handle, rec, 1 );
391             msiobj_release( &rec->hdr );
392         }
393
394         /* write out row 3, the table name + keys */
395         r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
396         if (r == ERROR_SUCCESS)
397         {
398             MSI_RecordSetStringW( rec, 0, table );
399             msi_export_record( handle, rec, 0 );
400             msiobj_release( &rec->hdr );
401         }
402
403         /* write out row 4 onwards, the data */
404         r = MSI_IterateRecords( view, 0, msi_export_row, handle );
405         msiobj_release( &view->hdr );
406     }
407
408     CloseHandle( handle );
409
410     return r;
411 }
412
413 /***********************************************************************
414  * MsiExportDatabaseW        [MSI.@]
415  *
416  * Writes a file containing the table data as tab separated ASCII.
417  *
418  * The format is as follows:
419  *
420  * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
421  * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
422  * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
423  *
424  * Followed by the data, starting at row 1 with one row per line
425  *
426  * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
427  */
428 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
429                LPCWSTR szFolder, LPCWSTR szFilename )
430 {
431     MSIDATABASE *db;
432     UINT r;
433
434     TRACE("%lx %s %s %s\n", handle, debugstr_w(szTable),
435           debugstr_w(szFolder), debugstr_w(szFilename));
436
437     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
438     if( !db )
439         return ERROR_INVALID_HANDLE;
440     r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
441     msiobj_release( &db->hdr );
442     return r;
443 }
444
445 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
446                LPCSTR szFolder, LPCSTR szFilename )
447 {
448     LPWSTR path = NULL, file = NULL, table = NULL;
449     UINT r = ERROR_OUTOFMEMORY;
450
451     TRACE("%lx %s %s %s\n", handle, debugstr_a(szTable),
452           debugstr_a(szFolder), debugstr_a(szFilename));
453
454     if( szTable )
455     {
456         table = strdupAtoW( szTable );
457         if( !table )
458             goto end;
459     }
460
461     if( szFolder )
462     {
463         path = strdupAtoW( szFolder );
464         if( !path )
465             goto end;
466     }
467
468     if( szFilename )
469     {
470         file = strdupAtoW( szFilename );
471         if( !file )
472             goto end;
473     }
474
475     r = MsiDatabaseExportW( handle, table, path, file );
476
477 end:
478     msi_free( table );
479     msi_free( path );
480     msi_free( file );
481
482     return r;
483 }
484
485 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
486 {
487     MSIDBSTATE ret = MSIDBSTATE_READ;
488     MSIDATABASE *db;
489
490     TRACE("%ld\n", handle);
491
492     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
493     if (!db)
494         return MSIDBSTATE_ERROR;
495     if (db->mode != MSIDBOPEN_READONLY )
496         ret = MSIDBSTATE_WRITE;
497     msiobj_release( &db->hdr );
498
499     return ret;
500 }