wintrust: Use helper function for setting confidence in SoftpubCheckCert.
[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 #include "msiserver.h"
38
39 #include "initguid.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(msi);
42
43 DEFINE_GUID( CLSID_MsiDatabase, 0x000c1084, 0x0000, 0x0000,
44              0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
45 DEFINE_GUID( CLSID_MsiPatch, 0x000c1086, 0x0000, 0x0000,
46              0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
47
48 /*
49  *  .MSI  file format
50  *
51  *  An .msi file is a structured storage file.
52  *  It contains a number of streams.
53  *  A stream for each table in the database.
54  *  Two streams for the string table in the database.
55  *  Any binary data in a table is a reference to a stream.
56  */
57
58 static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
59 {
60     MSIDATABASE *db = (MSIDATABASE *) arg;
61
62     msi_free(db->path);
63     free_cached_tables( db );
64     msi_free_transforms( db );
65     msi_destroy_stringtable( db->strings );
66     IStorage_Release( db->storage );
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, save_path;
81     STATSTG stat;
82     BOOL created = FALSE;
83     WCHAR path[MAX_PATH];
84
85     static const WCHAR backslash[] = {'\\',0};
86     static const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
87
88     TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
89
90     if( !pdb )
91         return ERROR_INVALID_PARAMETER;
92
93     if (szPersist - MSIDBOPEN_PATCHFILE >= MSIDBOPEN_READONLY &&
94         szPersist - MSIDBOPEN_PATCHFILE <= MSIDBOPEN_CREATEDIRECT)
95     {
96         TRACE("Database is a patch\n");
97         szPersist -= MSIDBOPEN_PATCHFILE;
98     }
99
100     save_path = szDBPath;
101     szMode = szPersist;
102     if( HIWORD( szPersist ) )
103     {
104         if (!CopyFileW( szDBPath, szPersist, FALSE ))
105             return ERROR_OPEN_FAILED;
106
107         szDBPath = szPersist;
108         szPersist = MSIDBOPEN_TRANSACT;
109         created = TRUE;
110     }
111
112     if( szPersist == MSIDBOPEN_READONLY )
113     {
114         r = StgOpenStorage( szDBPath, NULL,
115               STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
116     }
117     else if( szPersist == MSIDBOPEN_CREATE || szPersist == MSIDBOPEN_CREATEDIRECT )
118     {
119         /* FIXME: MSIDBOPEN_CREATE should case STGM_TRANSACTED flag to be
120          * used here: */
121         r = StgCreateDocfile( szDBPath,
122               STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg);
123         if( r == ERROR_SUCCESS )
124         {
125             IStorage_SetClass( stg, &CLSID_MsiDatabase );
126             /* create the _Tables stream */
127             r = write_stream_data(stg, szTables, NULL, 0, TRUE);
128             if (!FAILED(r))
129                 r = msi_init_string_table( stg );
130         }
131         created = TRUE;
132     }
133     else if( szPersist == MSIDBOPEN_TRANSACT )
134     {
135         /* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be
136          * used here: */
137         r = StgOpenStorage( szDBPath, NULL,
138               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
139     }
140     else if( szPersist == MSIDBOPEN_DIRECT )
141     {
142         r = StgOpenStorage( szDBPath, NULL,
143               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
144     }
145     else
146     {
147         ERR("unknown flag %p\n",szPersist);
148         return ERROR_INVALID_PARAMETER;
149     }
150
151     if( FAILED( r ) || !stg )
152     {
153         FIXME("open failed r = %08x for %s\n", r, debugstr_w(szDBPath));
154         return ERROR_FUNCTION_FAILED;
155     }
156
157     r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
158     if( FAILED( r ) )
159     {
160         FIXME("Failed to stat storage\n");
161         goto end;
162     }
163
164     if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
165          !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) ) 
166     {
167         ERR("storage GUID is not a MSI database GUID %s\n",
168              debugstr_guid(&stat.clsid) );
169         goto end;
170     }
171
172     db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
173                               MSI_CloseDatabase );
174     if( !db )
175     {
176         FIXME("Failed to allocate a handle\n");
177         goto end;
178     }
179
180     if (!strchrW( save_path, '\\' ))
181     {
182         GetCurrentDirectoryW( MAX_PATH, path );
183         lstrcatW( path, backslash );
184         lstrcatW( path, save_path );
185     }
186     else
187         lstrcpyW( path, save_path );
188
189     db->path = strdupW( path );
190
191     if( TRACE_ON( msi ) )
192         enum_stream_names( stg );
193
194     db->storage = stg;
195     db->mode = szMode;
196     if (created)
197         db->deletefile = strdupW( szDBPath );
198     else
199         db->deletefile = NULL;
200     list_init( &db->tables );
201     list_init( &db->transforms );
202
203     db->strings = msi_load_string_table( stg, &db->bytes_per_strref );
204     if( !db->strings )
205         goto end;
206
207     ret = ERROR_SUCCESS;
208
209     msiobj_addref( &db->hdr );
210     IStorage_AddRef( stg );
211     *pdb = db;
212
213 end:
214     if( db )
215         msiobj_release( &db->hdr );
216     if( stg )
217         IStorage_Release( stg );
218
219     return ret;
220 }
221
222 UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
223 {
224     MSIDATABASE *db;
225     UINT ret;
226
227     TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
228
229     ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
230     if( ret == ERROR_SUCCESS )
231     {
232         *phDB = alloc_msihandle( &db->hdr );
233         if (! *phDB)
234             ret = ERROR_NOT_ENOUGH_MEMORY;
235         msiobj_release( &db->hdr );
236     }
237
238     return ret;
239 }
240
241 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
242 {
243     HRESULT r = ERROR_FUNCTION_FAILED;
244     LPWSTR szwDBPath = NULL, szwPersist = NULL;
245
246     TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
247
248     if( szDBPath )
249     {
250         szwDBPath = strdupAtoW( szDBPath );
251         if( !szwDBPath )
252             goto end;
253     }
254
255     if( HIWORD(szPersist) )
256     {
257         szwPersist = strdupAtoW( szPersist );
258         if( !szwPersist )
259             goto end;
260     }
261     else
262         szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
263
264     r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
265
266 end:
267     if( HIWORD(szPersist) )
268         msi_free( szwPersist );
269     msi_free( szwDBPath );
270
271     return r;
272 }
273
274 static LPWSTR msi_read_text_archive(LPCWSTR path)
275 {
276     HANDLE file;
277     LPSTR data = NULL;
278     LPWSTR wdata = NULL;
279     DWORD read, size = 0;
280
281     file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
282     if (file == INVALID_HANDLE_VALUE)
283         return NULL;
284
285     size = GetFileSize( file, NULL );
286     data = msi_alloc( size + 1 );
287     if (!data)
288         goto done;
289
290     if (!ReadFile( file, data, size, &read, NULL ))
291         goto done;
292
293     data[size] = '\0';
294     wdata = strdupAtoW( data );
295
296 done:
297     CloseHandle( file );
298     msi_free( data );
299     return wdata;
300 }
301
302 static void msi_parse_line(LPWSTR *line, LPWSTR **entries, DWORD *num_entries)
303 {
304     LPWSTR ptr = *line, save;
305     DWORD i, count = 1;
306
307     *entries = NULL;
308
309     /* stay on this line */
310     while (*ptr && *ptr != '\n')
311     {
312         /* entries are separated by tabs */
313         if (*ptr == '\t')
314             count++;
315
316         ptr++;
317     }
318
319     *entries = msi_alloc(count * sizeof(LPWSTR));
320     if (!*entries)
321         return;
322
323     /* store pointers into the data */
324     for (i = 0, ptr = *line; i < count; i++)
325     {
326         while (*ptr && *ptr == '\r') ptr++;
327         save = ptr;
328
329         while (*ptr && *ptr != '\t' && *ptr != '\n' && *ptr != '\r') ptr++;
330
331         /* NULL-separate the data */
332         if (*ptr == '\n' || *ptr == '\r')
333         {
334             while (*ptr == '\n' || *ptr == '\r')
335                 *(ptr++) = '\0';
336         }
337         else if (*ptr)
338             *ptr++ = '\0';
339
340         (*entries)[i] = save;
341     }
342
343     /* move to the next line if there's more, else EOF */
344     *line = ptr;
345
346     if (num_entries)
347         *num_entries = count;
348 }
349
350 static LPWSTR msi_build_createsql_prelude(LPWSTR table)
351 {
352     LPWSTR prelude;
353     DWORD size;
354
355     static const WCHAR create_fmt[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
356
357     size = sizeof(create_fmt)/sizeof(create_fmt[0]) + lstrlenW(table) - 2;
358     prelude = msi_alloc(size * sizeof(WCHAR));
359     if (!prelude)
360         return NULL;
361
362     sprintfW(prelude, create_fmt, table);
363     return prelude;
364 }
365
366 static LPWSTR msi_build_createsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns)
367 {
368     LPWSTR columns, p;
369     LPCWSTR type;
370     DWORD sql_size = 1, i, len;
371     WCHAR expanded[128], *ptr;
372     WCHAR size[10], comma[2], extra[30];
373
374     static const WCHAR column_fmt[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
375     static const WCHAR size_fmt[] = {'(','%','s',')',0};
376     static const WCHAR type_char[] = {'C','H','A','R',0};
377     static const WCHAR type_int[] = {'I','N','T',0};
378     static const WCHAR type_long[] = {'L','O','N','G',0};
379     static const WCHAR type_notnull[] = {' ','N','O','T',' ','N','U','L','L',0};
380     static const WCHAR localizable[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0};
381
382     columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
383     if (!columns)
384         return NULL;
385
386     for (i = 0; i < num_columns; i++)
387     {
388         type = NULL;
389         comma[1] = size[0] = extra[0] = '\0';
390
391         if (i == num_columns - 1)
392             comma[0] = '\0';
393         else
394             comma[0] = ',';
395
396         ptr = &types[i][1];
397         len = atolW(ptr);
398         extra[0] = '\0';
399
400         switch (types[i][0])
401         {
402             case 'l':
403                 lstrcpyW(extra, type_notnull);
404             case 'L':
405                 lstrcatW(extra, localizable);
406                 type = type_char;
407                 sprintfW(size, size_fmt, ptr);
408                 break;
409             case 's':
410                 lstrcpyW(extra, type_notnull);
411             case 'S':
412                 type = type_char;
413                 sprintfW(size, size_fmt, ptr);
414                 break;
415             case 'i':
416                 lstrcpyW(extra, type_notnull);
417             case 'I':
418                 if (len == 2)
419                     type = type_int;
420                 else
421                     type = type_long;
422                 break;
423             default:
424                 ERR("Unknown type: %c\n", types[i][0]);
425                 msi_free(columns);
426                 return NULL;
427         }
428
429         sprintfW(expanded, column_fmt, columns_data[i], type, size, extra, comma);
430         sql_size += lstrlenW(expanded);
431
432         p = msi_realloc(columns, sql_size * sizeof(WCHAR));
433         if (!p)
434         {
435             msi_free(columns);
436             return NULL;
437         }
438         columns = p;
439
440         lstrcatW(columns, expanded);
441     }
442
443     return columns;
444 }
445
446 static LPWSTR msi_build_createsql_postlude(LPWSTR *primary_keys, DWORD num_keys)
447 {
448     LPWSTR postlude, keys, ptr;
449     DWORD size, key_size, i;
450
451     static const WCHAR key_fmt[] = {'`','%','s','`',',',' ',0};
452     static const WCHAR postlude_fmt[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0};
453
454     for (i = 0, size = 1; i < num_keys; i++)
455         size += lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) - 2;
456
457     keys = msi_alloc(size * sizeof(WCHAR));
458     if (!keys)
459         return NULL;
460
461     for (i = 0, ptr = keys; i < num_keys; i++)
462     {
463         key_size = lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) -2;
464         sprintfW(ptr, key_fmt, primary_keys[i]);
465         ptr += key_size;
466     }
467
468     /* remove final ', ' */
469     *(ptr - 2) = '\0';
470
471     size = lstrlenW(postlude_fmt) + size - 1;
472     postlude = msi_alloc(size * sizeof(WCHAR));
473     if (!postlude)
474         goto done;
475
476     sprintfW(postlude, postlude_fmt, keys);
477
478 done:
479     msi_free(keys);
480     return postlude;
481 }
482
483 static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_labels, DWORD num_columns)
484 {
485     UINT r;
486     DWORD size;
487     MSIQUERY *view;
488     LPWSTR create_sql;
489     LPWSTR prelude, columns_sql, postlude;
490
491     prelude = msi_build_createsql_prelude(labels[0]);
492     columns_sql = msi_build_createsql_columns(columns, types, num_columns);
493     postlude = msi_build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */
494
495     if (!prelude || !columns_sql || !postlude)
496         return ERROR_OUTOFMEMORY;
497
498     size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
499     create_sql = msi_alloc(size * sizeof(WCHAR));
500     if (!create_sql)
501         return ERROR_OUTOFMEMORY;
502
503     lstrcpyW(create_sql, prelude);
504     lstrcatW(create_sql, columns_sql);
505     lstrcatW(create_sql, postlude);
506
507     msi_free(prelude);
508     msi_free(columns_sql);
509     msi_free(postlude);
510
511     r = MSI_DatabaseOpenViewW( db, create_sql, &view );
512     msi_free(create_sql);
513
514     if (r != ERROR_SUCCESS)
515         return r;
516
517     r = MSI_ViewExecute(view, NULL);
518     MSI_ViewClose(view);
519     msiobj_release(&view->hdr);
520
521     return r;
522 }
523
524 static UINT construct_record(DWORD num_columns, LPWSTR *types,
525                              LPWSTR *data, MSIRECORD **rec)
526 {
527     UINT i;
528
529     *rec = MSI_CreateRecord(num_columns);
530     if (!*rec)
531         return ERROR_OUTOFMEMORY;
532
533     for (i = 0; i < num_columns; i++)
534     {
535         switch (types[i][0])
536         {
537             case 'L': case 'l': case 'S': case 's':
538                 MSI_RecordSetStringW(*rec, i + 1, data[i]);
539                 break;
540             case 'I': case 'i':
541                 if (*data[i])
542                     MSI_RecordSetInteger(*rec, i + 1, atoiW(data[i]));
543                 break;
544             default:
545                 ERR("Unhandled column type: %c\n", types[i][0]);
546                 msiobj_release(&(*rec)->hdr);
547                 return ERROR_FUNCTION_FAILED;
548         }
549     }
550
551     return ERROR_SUCCESS;
552 }
553
554 static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types,
555                                      LPWSTR *labels, LPWSTR **records,
556                                      int num_columns, int num_records)
557 {
558     UINT r;
559     DWORD i, size;
560     MSIQUERY *view;
561     MSIRECORD *rec;
562     LPWSTR query;
563
564     static const WCHAR select[] = {
565         'S','E','L','E','C','T',' ','*',' ',
566         'F','R','O','M',' ','`','%','s','`',0
567     };
568
569     size = lstrlenW(select) + lstrlenW(labels[0]) - 1;
570     query = msi_alloc(size * sizeof(WCHAR));
571     if (!query)
572         return ERROR_OUTOFMEMORY;
573
574     sprintfW(query, select, labels[0]);
575
576     r = MSI_DatabaseOpenViewW(db, query, &view);
577     msi_free(query);
578     if (r != ERROR_SUCCESS)
579         return r;
580
581     while (MSI_ViewFetch(view, &rec) != ERROR_NO_MORE_ITEMS)
582     {
583         r = MSI_ViewModify(view, MSIMODIFY_DELETE, rec);
584         if (r != ERROR_SUCCESS)
585             goto done;
586     }
587
588     for (i = 0; i < num_records; i++)
589     {
590         r = construct_record(num_columns, types, records[i], &rec);
591         if (r != ERROR_SUCCESS)
592             goto done;
593
594         r = MSI_ViewModify(view, MSIMODIFY_INSERT, rec);
595         if (r != ERROR_SUCCESS)
596         {
597             msiobj_release(&rec->hdr);
598             goto done;
599         }
600
601         msiobj_release(&rec->hdr);
602     }
603
604 done:
605     msiobj_release(&view->hdr);
606     return r;
607 }
608
609 UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
610 {
611     UINT r;
612     DWORD len, i;
613     DWORD num_labels, num_types;
614     DWORD num_columns, num_records = 0;
615     LPWSTR *columns, *types, *labels;
616     LPWSTR path, ptr, data;
617     LPWSTR **records = NULL;
618     LPWSTR **temp_records;
619
620     static const WCHAR backslash[] = {'\\',0};
621
622     TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
623
624     if( folder == NULL || file == NULL )
625         return ERROR_INVALID_PARAMETER;
626
627     len = lstrlenW(folder) + lstrlenW(backslash) + lstrlenW(file) + 1;
628     path = msi_alloc( len * sizeof(WCHAR) );
629     if (!path)
630         return ERROR_OUTOFMEMORY;
631
632     lstrcpyW( path, folder );
633     lstrcatW( path, backslash );
634     lstrcatW( path, file );
635
636     data = msi_read_text_archive( path );
637
638     ptr = data;
639     msi_parse_line( &ptr, &columns, &num_columns );
640     msi_parse_line( &ptr, &types, &num_types );
641     msi_parse_line( &ptr, &labels, &num_labels );
642
643     if (num_columns != num_types)
644     {
645         r = ERROR_FUNCTION_FAILED;
646         goto done;
647     }
648
649     records = msi_alloc(sizeof(LPWSTR *));
650     if (!records)
651     {
652         r = ERROR_OUTOFMEMORY;
653         goto done;
654     }
655
656     /* read in the table records */
657     while (*ptr)
658     {
659         msi_parse_line( &ptr, &records[num_records], NULL );
660
661         num_records++;
662         temp_records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *));
663         if (!temp_records)
664         {
665             r = ERROR_OUTOFMEMORY;
666             goto done;
667         }
668         records = temp_records;
669     }
670
671     if (!TABLE_Exists(db, labels[0]))
672     {
673         r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns );
674         if (r != ERROR_SUCCESS)
675         {
676             r = ERROR_FUNCTION_FAILED;
677             goto done;
678         }
679     }
680
681     r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records );
682
683 done:
684     msi_free(path);
685     msi_free(data);
686     msi_free(columns);
687     msi_free(types);
688     msi_free(labels);
689
690     for (i = 0; i < num_records; i++)
691         msi_free(records[i]);
692
693     msi_free(records);
694
695     return r;
696 }
697
698 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
699 {
700     MSIDATABASE *db;
701     UINT r;
702
703     TRACE("%lx %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
704
705     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
706     if( !db )
707     {
708         IWineMsiRemoteDatabase *remote_database;
709
710         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
711         if ( !remote_database )
712             return ERROR_INVALID_HANDLE;
713
714         IWineMsiRemoteDatabase_Release( remote_database );
715         WARN("MsiDatabaseImport not allowed during a custom action!\n");
716
717         return ERROR_SUCCESS;
718     }
719
720     r = MSI_DatabaseImport( db, szFolder, szFilename );
721     msiobj_release( &db->hdr );
722     return r;
723 }
724
725 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
726                LPCSTR szFolder, LPCSTR szFilename )
727 {
728     LPWSTR path = NULL, file = NULL;
729     UINT r = ERROR_OUTOFMEMORY;
730
731     TRACE("%lx %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
732
733     if( szFolder )
734     {
735         path = strdupAtoW( szFolder );
736         if( !path )
737             goto end;
738     }
739
740     if( szFilename )
741     {
742         file = strdupAtoW( szFilename );
743         if( !file )
744             goto end;
745     }
746
747     r = MsiDatabaseImportW( handle, path, file );
748
749 end:
750     msi_free( path );
751     msi_free( file );
752
753     return r;
754 }
755
756 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
757 {
758     UINT i, count, len, r = ERROR_SUCCESS;
759     const char *sep;
760     char *buffer;
761     DWORD sz;
762
763     len = 0x100;
764     buffer = msi_alloc( len );
765     if ( !buffer )
766         return ERROR_OUTOFMEMORY;
767
768     count = MSI_RecordGetFieldCount( row );
769     for ( i=start; i<=count; i++ )
770     {
771         sz = len;
772         r = MSI_RecordGetStringA( row, i, buffer, &sz );
773         if (r == ERROR_MORE_DATA)
774         {
775             char *p = msi_realloc( buffer, sz + 1 );
776             if (!p)
777                 break;
778             len = sz + 1;
779             buffer = p;
780         }
781         sz = len;
782         r = MSI_RecordGetStringA( row, i, buffer, &sz );
783         if (r != ERROR_SUCCESS)
784             break;
785
786         if (!WriteFile( handle, buffer, sz, &sz, NULL ))
787         {
788             r = ERROR_FUNCTION_FAILED;
789             break;
790         }
791
792         sep = (i < count) ? "\t" : "\r\n";
793         if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
794         {
795             r = ERROR_FUNCTION_FAILED;
796             break;
797         }
798     }
799     msi_free( buffer );
800     return r;
801 }
802
803 static UINT msi_export_row( MSIRECORD *row, void *arg )
804 {
805     return msi_export_record( arg, row, 1 );
806 }
807
808 static UINT msi_export_forcecodepage( HANDLE handle )
809 {
810     DWORD sz;
811
812     static const char data[] = "\r\n\r\n0\t_ForceCodepage\r\n";
813
814     FIXME("Read the codepage from the strings table!\n");
815
816     sz = lstrlenA(data) + 1;
817     if (!WriteFile(handle, data, sz, &sz, NULL))
818         return ERROR_FUNCTION_FAILED;
819
820     return ERROR_SUCCESS;
821 }
822
823 UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
824                LPCWSTR folder, LPCWSTR file )
825 {
826     static const WCHAR query[] = {
827         's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
828     static const WCHAR szbs[] = { '\\', 0 };
829     static const WCHAR forcecodepage[] = {
830         '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 };
831     MSIRECORD *rec = NULL;
832     MSIQUERY *view = NULL;
833     LPWSTR filename;
834     HANDLE handle;
835     UINT len, r;
836
837     TRACE("%p %s %s %s\n", db, debugstr_w(table),
838           debugstr_w(folder), debugstr_w(file) );
839
840     if( folder == NULL || file == NULL )
841         return ERROR_INVALID_PARAMETER;
842
843     len = lstrlenW(folder) + lstrlenW(file) + 2;
844     filename = msi_alloc(len * sizeof (WCHAR));
845     if (!filename)
846         return ERROR_OUTOFMEMORY;
847
848     lstrcpyW( filename, folder );
849     lstrcatW( filename, szbs );
850     lstrcatW( filename, file );
851
852     handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
853                           NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
854     msi_free( filename );
855     if (handle == INVALID_HANDLE_VALUE)
856         return ERROR_FUNCTION_FAILED;
857
858     if (!lstrcmpW( table, forcecodepage ))
859     {
860         r = msi_export_forcecodepage( handle );
861         goto done;
862     }
863
864     r = MSI_OpenQuery( db, &view, query, table );
865     if (r == ERROR_SUCCESS)
866     {
867         /* write out row 1, the column names */
868         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
869         if (r == ERROR_SUCCESS)
870         {
871             msi_export_record( handle, rec, 1 );
872             msiobj_release( &rec->hdr );
873         }
874
875         /* write out row 2, the column types */
876         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
877         if (r == ERROR_SUCCESS)
878         {
879             msi_export_record( handle, rec, 1 );
880             msiobj_release( &rec->hdr );
881         }
882
883         /* write out row 3, the table name + keys */
884         r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
885         if (r == ERROR_SUCCESS)
886         {
887             MSI_RecordSetStringW( rec, 0, table );
888             msi_export_record( handle, rec, 0 );
889             msiobj_release( &rec->hdr );
890         }
891
892         /* write out row 4 onwards, the data */
893         r = MSI_IterateRecords( view, 0, msi_export_row, handle );
894         msiobj_release( &view->hdr );
895     }
896
897 done:
898     CloseHandle( handle );
899     return r;
900 }
901
902 /***********************************************************************
903  * MsiExportDatabaseW        [MSI.@]
904  *
905  * Writes a file containing the table data as tab separated ASCII.
906  *
907  * The format is as follows:
908  *
909  * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
910  * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
911  * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
912  *
913  * Followed by the data, starting at row 1 with one row per line
914  *
915  * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
916  */
917 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
918                LPCWSTR szFolder, LPCWSTR szFilename )
919 {
920     MSIDATABASE *db;
921     UINT r;
922
923     TRACE("%lx %s %s %s\n", handle, debugstr_w(szTable),
924           debugstr_w(szFolder), debugstr_w(szFilename));
925
926     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
927     if( !db )
928     {
929         IWineMsiRemoteDatabase *remote_database;
930
931         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
932         if ( !remote_database )
933             return ERROR_INVALID_HANDLE;
934
935         IWineMsiRemoteDatabase_Release( remote_database );
936         WARN("MsiDatabaseExport not allowed during a custom action!\n");
937
938         return ERROR_SUCCESS;
939     }
940
941     r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
942     msiobj_release( &db->hdr );
943     return r;
944 }
945
946 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
947                LPCSTR szFolder, LPCSTR szFilename )
948 {
949     LPWSTR path = NULL, file = NULL, table = NULL;
950     UINT r = ERROR_OUTOFMEMORY;
951
952     TRACE("%lx %s %s %s\n", handle, debugstr_a(szTable),
953           debugstr_a(szFolder), debugstr_a(szFilename));
954
955     if( szTable )
956     {
957         table = strdupAtoW( szTable );
958         if( !table )
959             goto end;
960     }
961
962     if( szFolder )
963     {
964         path = strdupAtoW( szFolder );
965         if( !path )
966             goto end;
967     }
968
969     if( szFilename )
970     {
971         file = strdupAtoW( szFilename );
972         if( !file )
973             goto end;
974     }
975
976     r = MsiDatabaseExportW( handle, table, path, file );
977
978 end:
979     msi_free( table );
980     msi_free( path );
981     msi_free( file );
982
983     return r;
984 }
985
986 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
987 {
988     MSIDBSTATE ret = MSIDBSTATE_READ;
989     MSIDATABASE *db;
990
991     TRACE("%ld\n", handle);
992
993     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
994     if( !db )
995     {
996         IWineMsiRemoteDatabase *remote_database;
997
998         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
999         if ( !remote_database )
1000             return MSIDBSTATE_ERROR;
1001
1002         IWineMsiRemoteDatabase_Release( remote_database );
1003         WARN("MsiGetDatabaseState not allowed during a custom action!\n");
1004
1005         return MSIDBSTATE_READ;
1006     }
1007
1008     if (db->mode != MSIDBOPEN_READONLY )
1009         ret = MSIDBSTATE_WRITE;
1010     msiobj_release( &db->hdr );
1011
1012     return ret;
1013 }
1014
1015 typedef struct _msi_remote_database_impl {
1016     const IWineMsiRemoteDatabaseVtbl *lpVtbl;
1017     MSIHANDLE database;
1018     LONG refs;
1019 } msi_remote_database_impl;
1020
1021 static inline msi_remote_database_impl* mrd_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase* iface )
1022 {
1023     return (msi_remote_database_impl *)iface;
1024 }
1025
1026 static HRESULT WINAPI mrd_QueryInterface( IWineMsiRemoteDatabase *iface,
1027                                           REFIID riid,LPVOID *ppobj)
1028 {
1029     if( IsEqualCLSID( riid, &IID_IUnknown ) ||
1030         IsEqualCLSID( riid, &IID_IWineMsiRemoteDatabase ) )
1031     {
1032         IUnknown_AddRef( iface );
1033         *ppobj = iface;
1034         return S_OK;
1035     }
1036
1037     return E_NOINTERFACE;
1038 }
1039
1040 static ULONG WINAPI mrd_AddRef( IWineMsiRemoteDatabase *iface )
1041 {
1042     msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
1043
1044     return InterlockedIncrement( &This->refs );
1045 }
1046
1047 static ULONG WINAPI mrd_Release( IWineMsiRemoteDatabase *iface )
1048 {
1049     msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
1050     ULONG r;
1051
1052     r = InterlockedDecrement( &This->refs );
1053     if (r == 0)
1054     {
1055         MsiCloseHandle( This->database );
1056         msi_free( This );
1057     }
1058     return r;
1059 }
1060
1061 static HRESULT WINAPI mrd_IsTablePersistent( IWineMsiRemoteDatabase *iface,
1062                                              BSTR table, MSICONDITION *persistent )
1063 {
1064     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
1065     *persistent = MsiDatabaseIsTablePersistentW(This->database, (LPWSTR)table);
1066     return S_OK;
1067 }
1068
1069 static HRESULT WINAPI mrd_GetPrimaryKeys( IWineMsiRemoteDatabase *iface,
1070                                           BSTR table, MSIHANDLE *keys )
1071 {
1072     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
1073     UINT r = MsiDatabaseGetPrimaryKeysW(This->database, (LPWSTR)table, keys);
1074     return HRESULT_FROM_WIN32(r);
1075 }
1076
1077 static HRESULT WINAPI mrd_GetSummaryInformation( IWineMsiRemoteDatabase *iface,
1078                                                 UINT updatecount, MSIHANDLE *suminfo )
1079 {
1080     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
1081     UINT r = MsiGetSummaryInformationW(This->database, NULL, updatecount, suminfo);
1082     return HRESULT_FROM_WIN32(r);
1083 }
1084
1085 static HRESULT WINAPI mrd_OpenView( IWineMsiRemoteDatabase *iface,
1086                                     BSTR query, MSIHANDLE *view )
1087 {
1088     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
1089     UINT r = MsiDatabaseOpenViewW(This->database, (LPWSTR)query, view);
1090     return HRESULT_FROM_WIN32(r);
1091 }
1092
1093 static HRESULT WINAPI mrd_SetMsiHandle( IWineMsiRemoteDatabase *iface, MSIHANDLE handle )
1094 {
1095     msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
1096     This->database = handle;
1097     return S_OK;
1098 }
1099
1100 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl =
1101 {
1102     mrd_QueryInterface,
1103     mrd_AddRef,
1104     mrd_Release,
1105     mrd_IsTablePersistent,
1106     mrd_GetPrimaryKeys,
1107     mrd_GetSummaryInformation,
1108     mrd_OpenView,
1109     mrd_SetMsiHandle,
1110 };
1111
1112 HRESULT create_msi_remote_database( IUnknown *pOuter, LPVOID *ppObj )
1113 {
1114     msi_remote_database_impl *This;
1115
1116     This = msi_alloc( sizeof *This );
1117     if (!This)
1118         return E_OUTOFMEMORY;
1119
1120     This->lpVtbl = &msi_remote_database_vtbl;
1121     This->database = 0;
1122     This->refs = 1;
1123
1124     *ppObj = This;
1125
1126     return S_OK;
1127 }