gdi32: Add separate functions for releasing/freeing DCs to replace the standard GDI...
[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
61     msi_free(db->path);
62     free_cached_tables( db );
63     msi_free_transforms( db );
64     msi_destroy_stringtable( db->strings );
65     IStorage_Release( db->storage );
66     if (db->deletefile)
67     {
68         DeleteFileW( db->deletefile );
69         msi_free( db->deletefile );
70     }
71 }
72
73 UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
74 {
75     IStorage *stg = NULL;
76     HRESULT r;
77     MSIDATABASE *db = NULL;
78     UINT ret = ERROR_FUNCTION_FAILED;
79     LPCWSTR szMode, save_path;
80     STATSTG stat;
81     BOOL created = FALSE;
82     WCHAR path[MAX_PATH];
83
84     static const WCHAR backslash[] = {'\\',0};
85
86     TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
87
88     if( !pdb )
89         return ERROR_INVALID_PARAMETER;
90
91     save_path = szDBPath;
92     szMode = szPersist;
93     if( HIWORD( szPersist ) )
94     {
95         if (!CopyFileW( szDBPath, szPersist, FALSE ))
96             return ERROR_OPEN_FAILED;
97
98         szDBPath = szPersist;
99         szPersist = MSIDBOPEN_TRANSACT;
100         created = TRUE;
101     }
102
103     if( szPersist == MSIDBOPEN_READONLY )
104     {
105         r = StgOpenStorage( szDBPath, NULL,
106               STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
107     }
108     else if( szPersist == MSIDBOPEN_CREATE || szPersist == MSIDBOPEN_CREATEDIRECT )
109     {
110         /* FIXME: MSIDBOPEN_CREATE should case STGM_TRANSACTED flag to be
111          * used here: */
112         r = StgCreateDocfile( szDBPath,
113               STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg);
114         if( r == ERROR_SUCCESS )
115         {
116             IStorage_SetClass( stg, &CLSID_MsiDatabase );
117             r = msi_init_string_table( stg );
118         }
119         created = TRUE;
120     }
121     else if( szPersist == MSIDBOPEN_TRANSACT )
122     {
123         /* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be
124          * used here: */
125         r = StgOpenStorage( szDBPath, NULL,
126               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
127     }
128     else if( szPersist == MSIDBOPEN_DIRECT )
129     {
130         r = StgOpenStorage( szDBPath, NULL,
131               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
132     }
133     else
134     {
135         ERR("unknown flag %p\n",szPersist);
136         return ERROR_INVALID_PARAMETER;
137     }
138
139     if( FAILED( r ) || !stg )
140     {
141         FIXME("open failed r = %08x!\n",r);
142         return ERROR_FUNCTION_FAILED;
143     }
144
145     r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
146     if( FAILED( r ) )
147     {
148         FIXME("Failed to stat storage\n");
149         goto end;
150     }
151
152     if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
153          !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) ) 
154     {
155         ERR("storage GUID is not a MSI database GUID %s\n",
156              debugstr_guid(&stat.clsid) );
157         goto end;
158     }
159
160     db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
161                               MSI_CloseDatabase );
162     if( !db )
163     {
164         FIXME("Failed to allocate a handle\n");
165         goto end;
166     }
167
168     if (!strchrW( save_path, '\\' ))
169     {
170         GetCurrentDirectoryW( MAX_PATH, path );
171         lstrcatW( path, backslash );
172         lstrcatW( path, save_path );
173     }
174     else
175         lstrcpyW( path, save_path );
176
177     db->path = strdupW( path );
178
179     if( TRACE_ON( msi ) )
180         enum_stream_names( stg );
181
182     db->storage = stg;
183     db->mode = szMode;
184     if (created)
185         db->deletefile = strdupW( szDBPath );
186     else
187         db->deletefile = NULL;
188     list_init( &db->tables );
189     list_init( &db->transforms );
190
191     db->strings = msi_load_string_table( stg, &db->bytes_per_strref );
192     if( !db->strings )
193         goto end;
194
195     msi_table_set_strref( db->bytes_per_strref );
196     ret = ERROR_SUCCESS;
197
198     msiobj_addref( &db->hdr );
199     IStorage_AddRef( stg );
200     *pdb = db;
201
202 end:
203     if( db )
204         msiobj_release( &db->hdr );
205     if( stg )
206         IStorage_Release( stg );
207
208     return ret;
209 }
210
211 UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
212 {
213     MSIDATABASE *db;
214     UINT ret;
215
216     TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
217
218     ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
219     if( ret == ERROR_SUCCESS )
220     {
221         *phDB = alloc_msihandle( &db->hdr );
222         if (! *phDB)
223             ret = ERROR_NOT_ENOUGH_MEMORY;
224         msiobj_release( &db->hdr );
225     }
226
227     return ret;
228 }
229
230 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
231 {
232     HRESULT r = ERROR_FUNCTION_FAILED;
233     LPWSTR szwDBPath = NULL, szwPersist = NULL;
234
235     TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
236
237     if( szDBPath )
238     {
239         szwDBPath = strdupAtoW( szDBPath );
240         if( !szwDBPath )
241             goto end;
242     }
243
244     if( HIWORD(szPersist) )
245     {
246         szwPersist = strdupAtoW( szPersist );
247         if( !szwPersist )
248             goto end;
249     }
250     else
251         szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
252
253     r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
254
255 end:
256     if( HIWORD(szPersist) )
257         msi_free( szwPersist );
258     msi_free( szwDBPath );
259
260     return r;
261 }
262
263 static LPWSTR msi_read_text_archive(LPCWSTR path)
264 {
265     HANDLE file;
266     LPSTR data = NULL;
267     LPWSTR wdata = NULL;
268     DWORD read, size = 0;
269
270     file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
271     if (file == INVALID_HANDLE_VALUE)
272         return NULL;
273
274     size = GetFileSize( file, NULL );
275     data = msi_alloc( size + 1 );
276     if (!data)
277         goto done;
278
279     if (!ReadFile( file, data, size, &read, NULL ))
280         goto done;
281
282     data[size] = '\0';
283     wdata = strdupAtoW( data );
284
285 done:
286     CloseHandle( file );
287     msi_free( data );
288     return wdata;
289 }
290
291 static void msi_parse_line(LPWSTR *line, LPWSTR **entries, DWORD *num_entries)
292 {
293     LPWSTR ptr = *line, save;
294     DWORD i, count = 1;
295
296     *entries = NULL;
297
298     /* stay on this line */
299     while (*ptr && *ptr != '\n')
300     {
301         /* entries are separated by tabs */
302         if (*ptr == '\t')
303             count++;
304
305         ptr++;
306     }
307
308     *entries = msi_alloc(count * sizeof(LPWSTR));
309     if (!*entries)
310         return;
311
312     /* store pointers into the data */
313     for (i = 0, ptr = *line; i < count; i++)
314     {
315         save = ptr;
316
317         while (*ptr && *ptr != '\t' && *ptr != '\n') ptr++;
318
319         /* NULL-separate the data */
320         if (*ptr)
321             *ptr++ = '\0';
322
323         (*entries)[i] = save;
324     }
325
326     /* move to the next line if there's more, else EOF */
327     *line = ptr;
328
329     if (num_entries)
330         *num_entries = count;
331 }
332
333 static LPWSTR msi_build_createsql_prelude(LPWSTR table)
334 {
335     LPWSTR prelude;
336     DWORD size;
337
338     static const WCHAR create_fmt[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
339
340     size = sizeof(create_fmt) + lstrlenW(table) - 2;
341     prelude = msi_alloc(size * sizeof(WCHAR));
342     if (!prelude)
343         return NULL;
344
345     sprintfW(prelude, create_fmt, table);
346     return prelude;
347 }
348
349 static LPWSTR msi_build_createsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns)
350 {
351     LPWSTR columns;
352     LPCWSTR type;
353     DWORD sql_size = 1, i, len;
354     WCHAR expanded[128], *ptr;
355     WCHAR size[10], comma[2], extra[30];
356
357     static const WCHAR column_fmt[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
358     static const WCHAR size_fmt[] = {'(','%','s',')',0};
359     static const WCHAR type_char[] = {'C','H','A','R',0};
360     static const WCHAR type_int[] = {'I','N','T',0};
361     static const WCHAR type_long[] = {'L','O','N','G',0};
362     static const WCHAR type_notnull[] = {' ','N','O','T',' ','N','U','L','L',0};
363     static const WCHAR localizable[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0};
364
365     columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
366     if (!columns)
367         return NULL;
368
369     for (i = 0; i < num_columns; i++)
370     {
371         type = NULL;
372         comma[1] = size[0] = extra[0] = '\0';
373
374         if (i == num_columns - 1)
375             comma[0] = '\0';
376         else
377             comma[0] = ',';
378
379         ptr = &types[i][1];
380         len = atolW(ptr);
381         extra[0] = '\0';
382
383         switch (types[i][0])
384         {
385             case 'l':
386                 lstrcpyW(extra, type_notnull);
387             case 'L':
388                 lstrcatW(extra, localizable);
389                 type = type_char;
390                 sprintfW(size, size_fmt, ptr);
391                 break;
392             case 's':
393                 lstrcpyW(extra, type_notnull);
394             case 'S':
395                 type = type_char;
396                 sprintfW(size, size_fmt, ptr);
397                 break;
398             case 'i':
399                 lstrcpyW(extra, type_notnull);
400             case 'I':
401                 if (len == 2)
402                     type = type_int;
403                 else
404                     type = type_long;
405                 break;
406         }
407
408         sprintfW(expanded, column_fmt, columns_data[i], type, size, extra, comma);
409         sql_size += lstrlenW(expanded);
410
411         columns = msi_realloc(columns, sql_size * sizeof(WCHAR));
412         if (!columns)
413             return NULL;
414
415         lstrcatW(columns, expanded);
416     }
417
418     return columns;
419 }
420
421 static LPWSTR msi_build_createsql_postlude(LPWSTR *primary_keys, DWORD num_keys)
422 {
423     LPWSTR postlude, keys, ptr;
424     DWORD size, key_size, i;
425
426     static const WCHAR key_fmt[] = {'`','%','s','`',',',' ',0};
427     static const WCHAR postlude_fmt[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0};
428
429     for (i = 0, size = 1; i < num_keys; i++)
430         size += lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) - 2;
431
432     keys = msi_alloc(size * sizeof(WCHAR));
433     if (!keys)
434         return NULL;
435
436     for (i = 0, ptr = keys; i < num_keys; i++)
437     {
438         key_size = lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) -2;
439         sprintfW(ptr, key_fmt, primary_keys[i]);
440         ptr += key_size;
441     }
442
443     /* remove final ', ' */
444     *(ptr - 2) = '\0';
445
446     size = lstrlenW(postlude_fmt) + size - 1;
447     postlude = msi_alloc(size * sizeof(WCHAR));
448     if (!postlude)
449         goto done;
450
451     sprintfW(postlude, postlude_fmt, keys);
452
453 done:
454     msi_free(keys);
455     return postlude;
456 }
457
458 static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_labels, DWORD num_columns)
459 {
460     UINT r;
461     DWORD size;
462     MSIQUERY *view;
463     LPWSTR create_sql;
464     LPWSTR prelude, columns_sql, postlude;
465
466     prelude = msi_build_createsql_prelude(labels[0]);
467     columns_sql = msi_build_createsql_columns(columns, types, num_columns);
468     postlude = msi_build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */
469
470     if (!prelude || !columns_sql || !postlude)
471         return ERROR_OUTOFMEMORY;
472
473     size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
474     create_sql = msi_alloc(size * sizeof(WCHAR));
475     if (!create_sql)
476         return ERROR_OUTOFMEMORY;
477
478     lstrcpyW(create_sql, prelude);
479     lstrcatW(create_sql, columns_sql);
480     lstrcatW(create_sql, postlude);
481
482     msi_free(prelude);
483     msi_free(columns_sql);
484     msi_free(postlude);
485
486     r = MSI_DatabaseOpenViewW( db, create_sql, &view );
487     msi_free(create_sql);
488
489     if (r != ERROR_SUCCESS)
490         return r;
491
492     r = MSI_ViewExecute(view, NULL);
493     MSI_ViewClose(view);
494     msiobj_release(&view->hdr);
495
496     return r;
497 }
498
499 static LPWSTR msi_build_insertsql_prelude(LPWSTR table)
500 {
501     LPWSTR prelude;
502     DWORD size;
503
504     static const WCHAR insert_fmt[] = {'I','N','S','E','R','T',' ','I','N','T','O',' ','`','%','s','`',' ','(',' ',0};
505
506     size = sizeof(insert_fmt) + lstrlenW(table) - 2;
507     prelude = msi_alloc(size * sizeof(WCHAR));
508     if (!prelude)
509         return NULL;
510
511     sprintfW(prelude, insert_fmt, table);
512     return prelude;
513 }
514
515 static LPWSTR msi_build_insertsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns)
516 {
517     LPWSTR columns;
518     DWORD sql_size = 1, i;
519     WCHAR expanded[128];
520
521     static const WCHAR column_fmt[] =  {'`','%','s','`',',',' ',0};
522
523     columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
524     if (!columns)
525         return NULL;
526
527     for (i = 0; i < num_columns; i++)
528     {
529         sprintfW(expanded, column_fmt, columns_data[i]);
530         sql_size += lstrlenW(expanded);
531
532         if (i == num_columns - 1)
533         {
534             sql_size -= 2;
535             expanded[lstrlenW(expanded) - 2] = '\0';
536         }
537
538         columns = msi_realloc(columns, sql_size * sizeof(WCHAR));
539         if (!columns)
540             return NULL;
541
542         lstrcatW(columns, expanded);
543     }
544
545     return columns;
546 }
547
548 static LPWSTR msi_build_insertsql_data(LPWSTR **records, LPWSTR *types, DWORD num_columns, DWORD irec)
549 {
550     LPWSTR columns;
551     DWORD sql_size = 1, i;
552     WCHAR expanded[128];
553
554     static const WCHAR str_fmt[] = {'\'','%','s','\'',',',' ',0};
555     static const WCHAR int_fmt[] = {'%','s',',',' ',0};
556     static const WCHAR empty[] = {'\'','\'',',',' ',0};
557
558     columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
559     if (!columns)
560         return NULL;
561
562     for (i = 0; i < num_columns; i++)
563     {
564         switch (types[i][0])
565         {
566             case 'L': case 'l': case 'S': case 's':
567                 sprintfW(expanded, str_fmt, records[irec][i]);
568                 break;
569             case 'I': case 'i':
570                 if (*records[0][i])
571                     sprintfW(expanded, int_fmt, records[irec][i]);
572                 else
573                     lstrcpyW(expanded, empty);
574                 break;
575             default:
576                 return NULL;
577         }
578
579         if (i == num_columns - 1)
580             expanded[lstrlenW(expanded) - 2] = '\0';
581
582         sql_size += lstrlenW(expanded);
583         columns = msi_realloc(columns, sql_size * sizeof(WCHAR));
584         if (!columns)
585             return NULL;
586
587         lstrcatW(columns, expanded);
588     }
589
590     return columns;
591 }
592
593 static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types,
594                                      LPWSTR *labels, LPWSTR **records,
595                                      int num_columns, int num_records)
596 {
597     MSIQUERY *view;
598     LPWSTR insert_sql;
599     DWORD size, i;
600     UINT r = ERROR_SUCCESS;
601
602     static const WCHAR mid[] = {' ',')',' ','V','A','L','U','E','S',' ','(',' ',0};
603     static const WCHAR end[] = {' ',')',0};
604
605     LPWSTR prelude = msi_build_insertsql_prelude(labels[0]);
606     LPWSTR columns_sql = msi_build_insertsql_columns(columns, types, num_columns);
607     
608     for (i = 0; i < num_records; i++)
609     {
610         LPWSTR data = msi_build_insertsql_data(records, types, num_columns, i);
611
612         size = lstrlenW(prelude) + lstrlenW(columns_sql) + sizeof(mid) + lstrlenW(data) + sizeof(end) - 1; 
613         insert_sql = msi_alloc(size * sizeof(WCHAR));
614         if (!insert_sql)
615             return ERROR_OUTOFMEMORY;
616     
617         lstrcpyW(insert_sql, prelude);
618         lstrcatW(insert_sql, columns_sql);
619         lstrcatW(insert_sql, mid);
620         lstrcatW(insert_sql, data);
621         lstrcatW(insert_sql, end);
622
623         msi_free(data);
624
625         r = MSI_DatabaseOpenViewW( db, insert_sql, &view );
626         msi_free(insert_sql);
627
628         if (r != ERROR_SUCCESS)
629             goto done;
630
631         r = MSI_ViewExecute(view, NULL);
632         MSI_ViewClose(view);
633         msiobj_release(&view->hdr);
634     }
635
636 done:
637     msi_free(prelude);
638     msi_free(columns_sql);
639
640     return r;
641 }
642
643 UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
644 {
645     UINT r;
646     DWORD len, i;
647     DWORD num_labels;
648     DWORD num_columns, num_records = 0;
649     LPWSTR *columns, *types, *labels;
650     LPWSTR path, ptr, data;
651     LPWSTR **records;
652
653     static const WCHAR backslash[] = {'\\',0};
654
655     TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
656
657     if( folder == NULL || file == NULL )
658         return ERROR_INVALID_PARAMETER;
659
660     len = lstrlenW(folder) + lstrlenW(backslash) + lstrlenW(file) + 1;
661     path = msi_alloc( len * sizeof(WCHAR) );
662     if (!path)
663         return ERROR_OUTOFMEMORY;
664
665     lstrcpyW( path, folder );
666     lstrcatW( path, backslash );
667     lstrcatW( path, file );
668
669     data = msi_read_text_archive( path );
670
671     ptr = data;
672     msi_parse_line( &ptr, &columns, &num_columns );
673     msi_parse_line( &ptr, &types, NULL );
674     msi_parse_line( &ptr, &labels, &num_labels );
675
676     records = msi_alloc(sizeof(LPWSTR *));
677     if (!records)
678         return ERROR_OUTOFMEMORY;
679
680     /* read in the table records */
681     while (*ptr)
682     {
683         msi_parse_line( &ptr, &records[num_records], NULL );
684
685         num_records++;
686         records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *));
687         if (!records)
688             return ERROR_OUTOFMEMORY;
689     }
690
691     r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns );
692     if (r != ERROR_SUCCESS)
693         goto done;
694
695     r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records );
696
697 done:
698     msi_free(path);
699     msi_free(data);
700     msi_free(columns);
701     msi_free(types);
702     msi_free(labels);
703
704     for (i = 0; i < num_records; i++)
705         msi_free(records[i]);
706
707     msi_free(records);
708
709     return r;
710 }
711
712 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
713 {
714     MSIDATABASE *db;
715     UINT r;
716
717     TRACE("%lx %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
718
719     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
720     if( !db )
721         return ERROR_INVALID_HANDLE;
722     r = MSI_DatabaseImport( db, szFolder, szFilename );
723     msiobj_release( &db->hdr );
724     return r;
725 }
726
727 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
728                LPCSTR szFolder, LPCSTR szFilename )
729 {
730     LPWSTR path = NULL, file = NULL;
731     UINT r = ERROR_OUTOFMEMORY;
732
733     TRACE("%lx %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
734
735     if( szFolder )
736     {
737         path = strdupAtoW( szFolder );
738         if( !path )
739             goto end;
740     }
741
742     if( szFilename )
743     {
744         file = strdupAtoW( szFilename );
745         if( !file )
746             goto end;
747     }
748
749     r = MsiDatabaseImportW( handle, path, file );
750
751 end:
752     msi_free( path );
753     msi_free( file );
754
755     return r;
756 }
757
758 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
759 {
760     UINT i, count, len, r = ERROR_SUCCESS;
761     const char *sep;
762     char *buffer;
763     DWORD sz;
764
765     len = 0x100;
766     buffer = msi_alloc( len );
767     if ( !buffer )
768         return ERROR_OUTOFMEMORY;
769
770     count = MSI_RecordGetFieldCount( row );
771     for ( i=start; i<=count; i++ )
772     {
773         sz = len;
774         r = MSI_RecordGetStringA( row, i, buffer, &sz );
775         if (r == ERROR_MORE_DATA)
776         {
777             char *p = msi_realloc( buffer, sz + 1 );
778             if (!p)
779                 break;
780             len = sz + 1;
781             buffer = p;
782         }
783         sz = len;
784         r = MSI_RecordGetStringA( row, i, buffer, &sz );
785         if (r != ERROR_SUCCESS)
786             break;
787
788         if (!WriteFile( handle, buffer, sz, &sz, NULL ))
789         {
790             r = ERROR_FUNCTION_FAILED;
791             break;
792         }
793
794         sep = (i < count) ? "\t" : "\r\n";
795         if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
796         {
797             r = ERROR_FUNCTION_FAILED;
798             break;
799         }
800     }
801     msi_free( buffer );
802     return r;
803 }
804
805 static UINT msi_export_row( MSIRECORD *row, void *arg )
806 {
807     return msi_export_record( arg, row, 1 );
808 }
809
810 UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
811                LPCWSTR folder, LPCWSTR file )
812 {
813     static const WCHAR query[] = {
814         's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
815     static const WCHAR szbs[] = { '\\', 0 };
816     MSIRECORD *rec = NULL;
817     MSIQUERY *view = NULL;
818     LPWSTR filename;
819     HANDLE handle;
820     UINT len, r;
821
822     TRACE("%p %s %s %s\n", db, debugstr_w(table),
823           debugstr_w(folder), debugstr_w(file) );
824
825     if( folder == NULL || file == NULL )
826         return ERROR_INVALID_PARAMETER;
827
828     len = lstrlenW(folder) + lstrlenW(file) + 2;
829     filename = msi_alloc(len * sizeof (WCHAR));
830     if (!filename)
831         return ERROR_OUTOFMEMORY;
832
833     lstrcpyW( filename, folder );
834     lstrcatW( filename, szbs );
835     lstrcatW( filename, file );
836
837     handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
838                           NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
839     msi_free( filename );
840     if (handle == INVALID_HANDLE_VALUE)
841         return ERROR_FUNCTION_FAILED;
842
843     r = MSI_OpenQuery( db, &view, query, table );
844     if (r == ERROR_SUCCESS)
845     {
846         /* write out row 1, the column names */
847         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
848         if (r == ERROR_SUCCESS)
849         {
850             msi_export_record( handle, rec, 1 );
851             msiobj_release( &rec->hdr );
852         }
853
854         /* write out row 2, the column types */
855         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
856         if (r == ERROR_SUCCESS)
857         {
858             msi_export_record( handle, rec, 1 );
859             msiobj_release( &rec->hdr );
860         }
861
862         /* write out row 3, the table name + keys */
863         r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
864         if (r == ERROR_SUCCESS)
865         {
866             MSI_RecordSetStringW( rec, 0, table );
867             msi_export_record( handle, rec, 0 );
868             msiobj_release( &rec->hdr );
869         }
870
871         /* write out row 4 onwards, the data */
872         r = MSI_IterateRecords( view, 0, msi_export_row, handle );
873         msiobj_release( &view->hdr );
874     }
875
876     CloseHandle( handle );
877
878     return r;
879 }
880
881 /***********************************************************************
882  * MsiExportDatabaseW        [MSI.@]
883  *
884  * Writes a file containing the table data as tab separated ASCII.
885  *
886  * The format is as follows:
887  *
888  * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
889  * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
890  * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
891  *
892  * Followed by the data, starting at row 1 with one row per line
893  *
894  * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
895  */
896 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
897                LPCWSTR szFolder, LPCWSTR szFilename )
898 {
899     MSIDATABASE *db;
900     UINT r;
901
902     TRACE("%lx %s %s %s\n", handle, debugstr_w(szTable),
903           debugstr_w(szFolder), debugstr_w(szFilename));
904
905     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
906     if( !db )
907         return ERROR_INVALID_HANDLE;
908     r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
909     msiobj_release( &db->hdr );
910     return r;
911 }
912
913 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
914                LPCSTR szFolder, LPCSTR szFilename )
915 {
916     LPWSTR path = NULL, file = NULL, table = NULL;
917     UINT r = ERROR_OUTOFMEMORY;
918
919     TRACE("%lx %s %s %s\n", handle, debugstr_a(szTable),
920           debugstr_a(szFolder), debugstr_a(szFilename));
921
922     if( szTable )
923     {
924         table = strdupAtoW( szTable );
925         if( !table )
926             goto end;
927     }
928
929     if( szFolder )
930     {
931         path = strdupAtoW( szFolder );
932         if( !path )
933             goto end;
934     }
935
936     if( szFilename )
937     {
938         file = strdupAtoW( szFilename );
939         if( !file )
940             goto end;
941     }
942
943     r = MsiDatabaseExportW( handle, table, path, file );
944
945 end:
946     msi_free( table );
947     msi_free( path );
948     msi_free( file );
949
950     return r;
951 }
952
953 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
954 {
955     MSIDBSTATE ret = MSIDBSTATE_READ;
956     MSIDATABASE *db;
957
958     TRACE("%ld\n", handle);
959
960     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
961     if (!db)
962         return MSIDBSTATE_ERROR;
963     if (db->mode != MSIDBOPEN_READONLY )
964         ret = MSIDBSTATE_WRITE;
965     msiobj_release( &db->hdr );
966
967     return ret;
968 }