ole32: Don't fail in OleCreate if created object doesn't implement IRunnableObject...
[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 (%d)\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 = %08x!\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 static LPWSTR msi_read_text_archive(LPCWSTR path)
266 {
267     HANDLE file;
268     LPSTR data = NULL;
269     LPWSTR wdata = NULL;
270     DWORD read, size = 0;
271
272     file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
273     if (file == INVALID_HANDLE_VALUE)
274         return NULL;
275
276     size = GetFileSize( file, NULL );
277     data = msi_alloc( size + 1 );
278     if (!data)
279         goto done;
280
281     if (!ReadFile( file, data, size, &read, NULL ))
282         goto done;
283
284     data[size] = '\0';
285     wdata = strdupAtoW( data );
286
287 done:
288     CloseHandle( file );
289     msi_free( data );
290     return wdata;
291 }
292
293 static void msi_parse_line(LPWSTR *line, LPWSTR **entries, DWORD *num_entries)
294 {
295     LPWSTR ptr = *line, save;
296     DWORD i, count = 1;
297
298     *entries = NULL;
299
300     /* stay on this line */
301     while (*ptr && *ptr != '\n')
302     {
303         /* entries are separated by tabs */
304         if (*ptr == '\t')
305             count++;
306
307         ptr++;
308     }
309
310     *entries = msi_alloc(count * sizeof(LPWSTR));
311     if (!*entries)
312         return;
313
314     /* store pointers into the data */
315     for (i = 0, ptr = *line; i < count; i++)
316     {
317         save = ptr;
318
319         while (*ptr && *ptr != '\t' && *ptr != '\n') ptr++;
320
321         /* NULL-separate the data */
322         if (*ptr)
323             *ptr++ = '\0';
324
325         (*entries)[i] = save;
326     }
327
328     /* move to the next line if there's more, else EOF */
329     *line = ptr;
330
331     if (num_entries)
332         *num_entries = count;
333 }
334
335 static LPWSTR msi_build_createsql_prelude(LPWSTR table)
336 {
337     LPWSTR prelude;
338     DWORD size;
339
340     static const WCHAR create_fmt[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
341
342     size = sizeof(create_fmt) + lstrlenW(table) - 2;
343     prelude = msi_alloc(size * sizeof(WCHAR));
344     if (!prelude)
345         return NULL;
346
347     sprintfW(prelude, create_fmt, table);
348     return prelude;
349 }
350
351 static LPWSTR msi_build_createsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns)
352 {
353     LPWSTR columns;
354     LPCWSTR type;
355     DWORD sql_size = 1, i, len;
356     WCHAR expanded[128], *ptr;
357     WCHAR size[10], comma[2], extra[10];
358
359     static const WCHAR column_fmt[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
360     static const WCHAR size_fmt[] = {'(','%','s',')',0};
361     static const WCHAR type_char[] = {'C','H','A','R',0};
362     static const WCHAR type_int[] = {'I','N','T',0};
363     static const WCHAR type_long[] = {'L','O','N','G',0};
364     static const WCHAR type_notnull[] = {' ','N','O','T',' ','N','U','L','L',0};
365
366     columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
367     if (!columns)
368         return NULL;
369
370     for (i = 0; i < num_columns; i++)
371     {
372         type = NULL;
373         comma[1] = size[0] = extra[0] = '\0';
374
375         if (i == num_columns - 1)
376             comma[0] = '\0';
377         else
378             comma[0] = ',';
379
380         ptr = &types[i][1];
381         len = atolW(ptr);
382
383         switch (types[i][0])
384         {
385             case 'l': case 's':
386                 lstrcpyW(extra, type_notnull);
387             case 'L': case 'S':
388                 type = type_char;
389                 sprintfW(size, size_fmt, ptr);
390                 break;
391             case 'I': case 'i':
392                 if (len == 2)
393                     type = type_int;
394                 else
395                     type = type_long;
396                 break;
397         }
398
399         sprintfW(expanded, column_fmt, columns_data[i], type, size, extra, comma);
400         sql_size += lstrlenW(expanded);
401
402         columns = msi_realloc(columns, sql_size * sizeof(WCHAR));
403         if (!columns)
404             return NULL;
405
406         lstrcatW(columns, expanded);
407     }
408
409     return columns;
410 }
411
412 static LPWSTR msi_build_createsql_postlude(LPWSTR primary_key)
413 {
414     LPWSTR postlude;
415     DWORD size;
416
417     static const WCHAR postlude_fmt[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','`','%','s','`',')',' ','H','O','L','D',0};
418
419     size = sizeof(postlude_fmt) + lstrlenW(primary_key) - 2;
420     postlude = msi_alloc(size * sizeof(WCHAR));
421     if (!postlude)
422         return NULL;
423
424     sprintfW(postlude, postlude_fmt, primary_key);
425     return postlude;
426 }
427
428 static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_columns)
429 {
430     UINT r;
431     DWORD size;
432     MSIQUERY *view;
433     LPWSTR create_sql;
434     LPWSTR prelude, columns_sql, postlude;
435
436     prelude = msi_build_createsql_prelude(labels[0]);
437     columns_sql = msi_build_createsql_columns(columns, types, num_columns);
438     postlude = msi_build_createsql_postlude(labels[1]);
439
440     if (!prelude || !columns_sql || !postlude)
441         return ERROR_OUTOFMEMORY;
442
443     size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
444     create_sql = msi_alloc(size * sizeof(WCHAR));
445     if (!create_sql)
446         return ERROR_OUTOFMEMORY;
447
448     lstrcpyW(create_sql, prelude);
449     lstrcatW(create_sql, columns_sql);
450     lstrcatW(create_sql, postlude);
451
452     msi_free(prelude);
453     msi_free(columns_sql);
454     msi_free(postlude);
455
456     r = MSI_DatabaseOpenViewW( db, create_sql, &view );
457     msi_free(create_sql);
458
459     if (r != ERROR_SUCCESS)
460         return r;
461
462     r = MSI_ViewExecute(view, NULL);
463     MSI_ViewClose(view);
464     msiobj_release(&view->hdr);
465
466     return r;
467 }
468
469 static LPWSTR msi_build_insertsql_prelude(LPWSTR table)
470 {
471     LPWSTR prelude;
472     DWORD size;
473
474     static const WCHAR insert_fmt[] = {'I','N','S','E','R','T',' ','I','N','T','O',' ','`','%','s','`',' ','(',' ',0};
475
476     size = sizeof(insert_fmt) + lstrlenW(table) - 2;
477     prelude = msi_alloc(size * sizeof(WCHAR));
478     if (!prelude)
479         return NULL;
480
481     sprintfW(prelude, insert_fmt, table);
482     return prelude;
483 }
484
485 static LPWSTR msi_build_insertsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns)
486 {
487     LPWSTR columns;
488     DWORD sql_size = 1, i;
489     WCHAR expanded[128];
490
491     static const WCHAR column_fmt[] =  {'`','%','s','`',',',' ',0};
492
493     columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
494     if (!columns)
495         return NULL;
496
497     for (i = 0; i < num_columns; i++)
498     {
499         sprintfW(expanded, column_fmt, columns_data[i]);
500         sql_size += lstrlenW(expanded);
501
502         if (i == num_columns - 1)
503         {
504             sql_size -= 2;
505             expanded[lstrlenW(expanded) - 2] = '\0';
506         }
507
508         columns = msi_realloc(columns, sql_size * sizeof(WCHAR));
509         if (!columns)
510             return NULL;
511
512         lstrcatW(columns, expanded);
513     }
514
515     return columns;
516 }
517
518 static LPWSTR msi_build_insertsql_data(LPWSTR **records, LPWSTR *types, DWORD num_columns, DWORD irec)
519 {
520     LPWSTR columns;
521     DWORD sql_size = 1, i;
522     WCHAR expanded[128];
523
524     static const WCHAR str_fmt[] = {'\'','%','s','\'',',',' ',0};
525     static const WCHAR int_fmt[] = {'%','s',',',' ',0};
526     static const WCHAR empty[] = {'\'','\'',',',' ',0};
527
528     columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
529     if (!columns)
530         return NULL;
531
532     for (i = 0; i < num_columns; i++)
533     {
534         switch (types[i][0])
535         {
536             case 'L': case 'l': case 'S': case 's':
537                 sprintfW(expanded, str_fmt, records[irec][i]);
538                 break;
539             case 'I': case 'i':
540                 if (*records[0][i])
541                     sprintfW(expanded, int_fmt, records[irec][i]);
542                 else
543                     lstrcpyW(expanded, empty);
544                 break;
545             default:
546                 return NULL;
547         }
548
549         if (i == num_columns - 1)
550             expanded[lstrlenW(expanded) - 2] = '\0';
551
552         sql_size += lstrlenW(expanded);
553         columns = msi_realloc(columns, sql_size * sizeof(WCHAR));
554         if (!columns)
555             return NULL;
556
557         lstrcatW(columns, expanded);
558     }
559
560     return columns;
561 }
562
563 static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types,
564                                      LPWSTR *labels, LPWSTR **records,
565                                      int num_columns, int num_records)
566 {
567     MSIQUERY *view;
568     LPWSTR insert_sql;
569     DWORD size, i;
570     UINT r = ERROR_SUCCESS;
571
572     static const WCHAR mid[] = {' ',')',' ','V','A','L','U','E','S',' ','(',' ',0};
573     static const WCHAR end[] = {' ',')',0};
574
575     LPWSTR prelude = msi_build_insertsql_prelude(labels[0]);
576     LPWSTR columns_sql = msi_build_insertsql_columns(columns, types, num_columns);
577     
578     for (i = 0; i < num_records; i++)
579     {
580         LPWSTR data = msi_build_insertsql_data(records, types, num_columns, i);
581
582         size = lstrlenW(prelude) + lstrlenW(columns_sql) + sizeof(mid) + lstrlenW(data) + sizeof(end) - 1; 
583         insert_sql = msi_alloc(size * sizeof(WCHAR));
584         if (!insert_sql)
585             return ERROR_OUTOFMEMORY;
586     
587         lstrcpyW(insert_sql, prelude);
588         lstrcatW(insert_sql, columns_sql);
589         lstrcatW(insert_sql, mid);
590         lstrcatW(insert_sql, data);
591         lstrcatW(insert_sql, end);
592
593         msi_free(data);
594
595         r = MSI_DatabaseOpenViewW( db, insert_sql, &view );
596         msi_free(insert_sql);
597
598         if (r != ERROR_SUCCESS)
599             goto done;
600
601         r = MSI_ViewExecute(view, NULL);
602         MSI_ViewClose(view);
603         msiobj_release(&view->hdr);
604     }
605
606 done:
607     msi_free(prelude);
608     msi_free(columns_sql);
609
610     return r;
611 }
612
613 UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
614 {
615     UINT r;
616     DWORD len, i;
617     DWORD num_columns, num_records = 0;
618     LPWSTR *columns, *types, *labels;
619     LPWSTR path, ptr, data;
620     LPWSTR **records;
621
622     static const WCHAR backslash[] = {'\\',0};
623
624     TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
625
626     if( folder == NULL || file == NULL )
627         return ERROR_INVALID_PARAMETER;
628
629     len = lstrlenW(folder) + lstrlenW(backslash) + lstrlenW(file) + 1;
630     path = msi_alloc( len * sizeof(WCHAR) );
631     if (!path)
632         return ERROR_OUTOFMEMORY;
633
634     lstrcpyW( path, folder );
635     lstrcatW( path, backslash );
636     lstrcatW( path, file );
637
638     data = msi_read_text_archive( path );
639
640     ptr = data;
641     msi_parse_line( &ptr, &columns, &num_columns );
642     msi_parse_line( &ptr, &types, NULL );
643     msi_parse_line( &ptr, &labels, NULL );
644
645     records = msi_alloc(sizeof(LPWSTR *));
646     if (!records)
647         return ERROR_OUTOFMEMORY;
648
649     /* read in the table records */
650     while (*ptr)
651     {
652         msi_parse_line( &ptr, &records[num_records], NULL );
653
654         num_records++;
655         records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *));
656         if (!records)
657             return ERROR_OUTOFMEMORY;
658     }
659
660     r = msi_add_table_to_db( db, columns, types, labels, num_columns );
661     if (r != ERROR_SUCCESS)
662         goto done;
663
664     r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records );
665
666 done:
667     msi_free(path);
668     msi_free(data);
669     msi_free(columns);
670     msi_free(types);
671
672     for (i = 0; i < num_records; i++)
673         msi_free(records[i]);
674
675     msi_free(records);
676
677     return r;
678 }
679
680 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
681 {
682     MSIDATABASE *db;
683     UINT r;
684
685     TRACE("%lx %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
686
687     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
688     if( !db )
689         return ERROR_INVALID_HANDLE;
690     r = MSI_DatabaseImport( db, szFolder, szFilename );
691     msiobj_release( &db->hdr );
692     return r;
693 }
694
695 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
696                LPCSTR szFolder, LPCSTR szFilename )
697 {
698     LPWSTR path = NULL, file = NULL;
699     UINT r = ERROR_OUTOFMEMORY;
700
701     TRACE("%lx %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
702
703     if( szFolder )
704     {
705         path = strdupAtoW( szFolder );
706         if( !path )
707             goto end;
708     }
709
710     if( szFilename )
711     {
712         file = strdupAtoW( szFilename );
713         if( !file )
714             goto end;
715     }
716
717     r = MsiDatabaseImportW( handle, path, file );
718
719 end:
720     msi_free( path );
721     msi_free( file );
722
723     return r;
724 }
725
726 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
727 {
728     UINT i, count, len, r = ERROR_SUCCESS;
729     const char *sep;
730     char *buffer;
731     DWORD sz;
732
733     len = 0x100;
734     buffer = msi_alloc( len );
735     if ( !buffer )
736         return ERROR_OUTOFMEMORY;
737
738     count = MSI_RecordGetFieldCount( row );
739     for ( i=start; i<=count; i++ )
740     {
741         sz = len;
742         r = MSI_RecordGetStringA( row, i, buffer, &sz );
743         if (r == ERROR_MORE_DATA)
744         {
745             char *p = msi_realloc( buffer, sz + 1 );
746             if (!p)
747                 break;
748             len = sz + 1;
749             buffer = p;
750         }
751         sz = len;
752         r = MSI_RecordGetStringA( row, i, buffer, &sz );
753         if (r != ERROR_SUCCESS)
754             break;
755
756         if (!WriteFile( handle, buffer, sz, &sz, NULL ))
757         {
758             r = ERROR_FUNCTION_FAILED;
759             break;
760         }
761
762         sep = (i < count) ? "\t" : "\r\n";
763         if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
764         {
765             r = ERROR_FUNCTION_FAILED;
766             break;
767         }
768     }
769     msi_free( buffer );
770     return r;
771 }
772
773 static UINT msi_export_row( MSIRECORD *row, void *arg )
774 {
775     return msi_export_record( arg, row, 1 );
776 }
777
778 UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
779                LPCWSTR folder, LPCWSTR file )
780 {
781     static const WCHAR query[] = {
782         's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
783     static const WCHAR szbs[] = { '\\', 0 };
784     MSIRECORD *rec = NULL;
785     MSIQUERY *view = NULL;
786     LPWSTR filename;
787     HANDLE handle;
788     UINT len, r;
789
790     TRACE("%p %s %s %s\n", db, debugstr_w(table),
791           debugstr_w(folder), debugstr_w(file) );
792
793     if( folder == NULL || file == NULL )
794         return ERROR_INVALID_PARAMETER;
795
796     len = lstrlenW(folder) + lstrlenW(file) + 2;
797     filename = msi_alloc(len * sizeof (WCHAR));
798     if (!filename)
799         return ERROR_OUTOFMEMORY;
800
801     lstrcpyW( filename, folder );
802     lstrcatW( filename, szbs );
803     lstrcatW( filename, file );
804
805     handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
806                           NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
807     msi_free( filename );
808     if (handle == INVALID_HANDLE_VALUE)
809         return ERROR_FUNCTION_FAILED;
810
811     r = MSI_OpenQuery( db, &view, query, table );
812     if (r == ERROR_SUCCESS)
813     {
814         /* write out row 1, the column names */
815         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
816         if (r == ERROR_SUCCESS)
817         {
818             msi_export_record( handle, rec, 1 );
819             msiobj_release( &rec->hdr );
820         }
821
822         /* write out row 2, the column types */
823         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
824         if (r == ERROR_SUCCESS)
825         {
826             msi_export_record( handle, rec, 1 );
827             msiobj_release( &rec->hdr );
828         }
829
830         /* write out row 3, the table name + keys */
831         r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
832         if (r == ERROR_SUCCESS)
833         {
834             MSI_RecordSetStringW( rec, 0, table );
835             msi_export_record( handle, rec, 0 );
836             msiobj_release( &rec->hdr );
837         }
838
839         /* write out row 4 onwards, the data */
840         r = MSI_IterateRecords( view, 0, msi_export_row, handle );
841         msiobj_release( &view->hdr );
842     }
843
844     CloseHandle( handle );
845
846     return r;
847 }
848
849 /***********************************************************************
850  * MsiExportDatabaseW        [MSI.@]
851  *
852  * Writes a file containing the table data as tab separated ASCII.
853  *
854  * The format is as follows:
855  *
856  * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
857  * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
858  * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
859  *
860  * Followed by the data, starting at row 1 with one row per line
861  *
862  * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
863  */
864 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
865                LPCWSTR szFolder, LPCWSTR szFilename )
866 {
867     MSIDATABASE *db;
868     UINT r;
869
870     TRACE("%lx %s %s %s\n", handle, debugstr_w(szTable),
871           debugstr_w(szFolder), debugstr_w(szFilename));
872
873     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
874     if( !db )
875         return ERROR_INVALID_HANDLE;
876     r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
877     msiobj_release( &db->hdr );
878     return r;
879 }
880
881 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
882                LPCSTR szFolder, LPCSTR szFilename )
883 {
884     LPWSTR path = NULL, file = NULL, table = NULL;
885     UINT r = ERROR_OUTOFMEMORY;
886
887     TRACE("%lx %s %s %s\n", handle, debugstr_a(szTable),
888           debugstr_a(szFolder), debugstr_a(szFilename));
889
890     if( szTable )
891     {
892         table = strdupAtoW( szTable );
893         if( !table )
894             goto end;
895     }
896
897     if( szFolder )
898     {
899         path = strdupAtoW( szFolder );
900         if( !path )
901             goto end;
902     }
903
904     if( szFilename )
905     {
906         file = strdupAtoW( szFilename );
907         if( !file )
908             goto end;
909     }
910
911     r = MsiDatabaseExportW( handle, table, path, file );
912
913 end:
914     msi_free( table );
915     msi_free( path );
916     msi_free( file );
917
918     return r;
919 }
920
921 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
922 {
923     MSIDBSTATE ret = MSIDBSTATE_READ;
924     MSIDATABASE *db;
925
926     TRACE("%ld\n", handle);
927
928     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
929     if (!db)
930         return MSIDBSTATE_ERROR;
931     if (db->mode != MSIDBOPEN_READONLY )
932         ret = MSIDBSTATE_WRITE;
933     msiobj_release( &db->hdr );
934
935     return ret;
936 }