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