crypt32: Make sure we show Unicode characters (Dutch translation).
[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 #include "query.h"
39
40 #include "initguid.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(msi);
43
44 /*
45  *  .MSI  file format
46  *
47  *  An .msi file is a structured storage file.
48  *  It contains a number of streams.
49  *  A stream for each table in the database.
50  *  Two streams for the string table in the database.
51  *  Any binary data in a table is a reference to a stream.
52  */
53
54 static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
55 {
56     MSIDATABASE *db = (MSIDATABASE *) arg;
57
58     msi_free(db->path);
59     free_cached_tables( db );
60     msi_free_transforms( db );
61     msi_destroy_stringtable( db->strings );
62     IStorage_Release( db->storage );
63     if (db->deletefile)
64     {
65         DeleteFileW( db->deletefile );
66         msi_free( db->deletefile );
67     }
68 }
69
70 UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
71 {
72     IStorage *stg = NULL;
73     HRESULT r;
74     MSIDATABASE *db = NULL;
75     UINT ret = ERROR_FUNCTION_FAILED;
76     LPCWSTR szMode, save_path;
77     STATSTG stat;
78     BOOL created = FALSE;
79     WCHAR path[MAX_PATH];
80
81     static const WCHAR backslash[] = {'\\',0};
82     static const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
83
84     TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
85
86     if( !pdb )
87         return ERROR_INVALID_PARAMETER;
88
89     if (szPersist - MSIDBOPEN_PATCHFILE >= MSIDBOPEN_READONLY &&
90         szPersist - MSIDBOPEN_PATCHFILE <= MSIDBOPEN_CREATEDIRECT)
91     {
92         TRACE("Database is a patch\n");
93         szPersist -= MSIDBOPEN_PATCHFILE;
94     }
95
96     save_path = szDBPath;
97     szMode = szPersist;
98     if( HIWORD( szPersist ) )
99     {
100         if (!CopyFileW( szDBPath, szPersist, FALSE ))
101             return ERROR_OPEN_FAILED;
102
103         szDBPath = szPersist;
104         szPersist = MSIDBOPEN_TRANSACT;
105         created = TRUE;
106     }
107
108     if( szPersist == MSIDBOPEN_READONLY )
109     {
110         r = StgOpenStorage( szDBPath, NULL,
111               STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
112     }
113     else if( szPersist == MSIDBOPEN_CREATE || szPersist == MSIDBOPEN_CREATEDIRECT )
114     {
115         /* FIXME: MSIDBOPEN_CREATE should case STGM_TRANSACTED flag to be
116          * used here: */
117         r = StgCreateDocfile( szDBPath,
118               STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg);
119         if( r == ERROR_SUCCESS )
120         {
121             IStorage_SetClass( stg, &CLSID_MsiDatabase );
122             /* create the _Tables stream */
123             r = write_stream_data(stg, szTables, NULL, 0, TRUE);
124             if (SUCCEEDED(r))
125                 r = msi_init_string_table( stg );
126         }
127         created = TRUE;
128     }
129     else if( szPersist == MSIDBOPEN_TRANSACT )
130     {
131         /* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be
132          * used here: */
133         r = StgOpenStorage( szDBPath, NULL,
134               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
135     }
136     else if( szPersist == MSIDBOPEN_DIRECT )
137     {
138         r = StgOpenStorage( szDBPath, NULL,
139               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
140     }
141     else
142     {
143         ERR("unknown flag %p\n",szPersist);
144         return ERROR_INVALID_PARAMETER;
145     }
146
147     if( FAILED( r ) || !stg )
148     {
149         FIXME("open failed r = %08x for %s\n", r, debugstr_w(szDBPath));
150         return ERROR_FUNCTION_FAILED;
151     }
152
153     r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
154     if( FAILED( r ) )
155     {
156         FIXME("Failed to stat storage\n");
157         goto end;
158     }
159
160     if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
161          !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) &&
162          !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
163     {
164         ERR("storage GUID is not a MSI database GUID %s\n",
165              debugstr_guid(&stat.clsid) );
166         goto end;
167     }
168
169     db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
170                               MSI_CloseDatabase );
171     if( !db )
172     {
173         FIXME("Failed to allocate a handle\n");
174         goto end;
175     }
176
177     if (!strchrW( save_path, '\\' ))
178     {
179         GetCurrentDirectoryW( MAX_PATH, path );
180         lstrcatW( path, backslash );
181         lstrcatW( path, save_path );
182     }
183     else
184         lstrcpyW( path, save_path );
185
186     db->path = strdupW( path );
187
188     if( TRACE_ON( msi ) )
189         enum_stream_names( stg );
190
191     db->storage = stg;
192     db->mode = szMode;
193     if (created)
194         db->deletefile = strdupW( szDBPath );
195     else
196         db->deletefile = NULL;
197     list_init( &db->tables );
198     list_init( &db->transforms );
199
200     db->strings = msi_load_string_table( stg, &db->bytes_per_strref );
201     if( !db->strings )
202         goto end;
203
204     ret = ERROR_SUCCESS;
205
206     msiobj_addref( &db->hdr );
207     IStorage_AddRef( stg );
208     *pdb = db;
209
210 end:
211     if( db )
212         msiobj_release( &db->hdr );
213     if( stg )
214         IStorage_Release( stg );
215
216     return ret;
217 }
218
219 UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
220 {
221     MSIDATABASE *db;
222     UINT ret;
223
224     TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
225
226     ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
227     if( ret == ERROR_SUCCESS )
228     {
229         *phDB = alloc_msihandle( &db->hdr );
230         if (! *phDB)
231             ret = ERROR_NOT_ENOUGH_MEMORY;
232         msiobj_release( &db->hdr );
233     }
234
235     return ret;
236 }
237
238 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
239 {
240     HRESULT r = ERROR_FUNCTION_FAILED;
241     LPWSTR szwDBPath = NULL, szwPersist = NULL;
242
243     TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
244
245     if( szDBPath )
246     {
247         szwDBPath = strdupAtoW( szDBPath );
248         if( !szwDBPath )
249             goto end;
250     }
251
252     if( HIWORD(szPersist) )
253     {
254         szwPersist = strdupAtoW( szPersist );
255         if( !szwPersist )
256             goto end;
257     }
258     else
259         szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
260
261     r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
262
263 end:
264     if( HIWORD(szPersist) )
265         msi_free( szwPersist );
266     msi_free( szwDBPath );
267
268     return r;
269 }
270
271 static LPWSTR msi_read_text_archive(LPCWSTR path)
272 {
273     HANDLE file;
274     LPSTR data = NULL;
275     LPWSTR wdata = NULL;
276     DWORD read, size = 0;
277
278     file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
279     if (file == INVALID_HANDLE_VALUE)
280         return NULL;
281
282     size = GetFileSize( file, NULL );
283     data = msi_alloc( size + 1 );
284     if (!data)
285         goto done;
286
287     if (!ReadFile( file, data, size, &read, NULL ))
288         goto done;
289
290     data[size] = '\0';
291     wdata = strdupAtoW( data );
292
293 done:
294     CloseHandle( file );
295     msi_free( data );
296     return wdata;
297 }
298
299 static void msi_parse_line(LPWSTR *line, LPWSTR **entries, DWORD *num_entries)
300 {
301     LPWSTR ptr = *line, save;
302     DWORD i, count = 1;
303
304     *entries = NULL;
305
306     /* stay on this line */
307     while (*ptr && *ptr != '\n')
308     {
309         /* entries are separated by tabs */
310         if (*ptr == '\t')
311             count++;
312
313         ptr++;
314     }
315
316     *entries = msi_alloc(count * sizeof(LPWSTR));
317     if (!*entries)
318         return;
319
320     /* store pointers into the data */
321     for (i = 0, ptr = *line; i < count; i++)
322     {
323         while (*ptr && *ptr == '\r') ptr++;
324         save = ptr;
325
326         while (*ptr && *ptr != '\t' && *ptr != '\n' && *ptr != '\r') ptr++;
327
328         /* NULL-separate the data */
329         if (*ptr == '\n' || *ptr == '\r')
330         {
331             while (*ptr == '\n' || *ptr == '\r')
332                 *(ptr++) = '\0';
333         }
334         else if (*ptr)
335             *ptr++ = '\0';
336
337         (*entries)[i] = save;
338     }
339
340     /* move to the next line if there's more, else EOF */
341     *line = ptr;
342
343     if (num_entries)
344         *num_entries = count;
345 }
346
347 static LPWSTR msi_build_createsql_prelude(LPWSTR table)
348 {
349     LPWSTR prelude;
350     DWORD size;
351
352     static const WCHAR create_fmt[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
353
354     size = sizeof(create_fmt)/sizeof(create_fmt[0]) + lstrlenW(table) - 2;
355     prelude = msi_alloc(size * sizeof(WCHAR));
356     if (!prelude)
357         return NULL;
358
359     sprintfW(prelude, create_fmt, table);
360     return prelude;
361 }
362
363 static LPWSTR msi_build_createsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns)
364 {
365     LPWSTR columns, p;
366     LPCWSTR type;
367     DWORD sql_size = 1, i, len;
368     WCHAR expanded[128], *ptr;
369     WCHAR size[10], comma[2], extra[30];
370
371     static const WCHAR column_fmt[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
372     static const WCHAR size_fmt[] = {'(','%','s',')',0};
373     static const WCHAR type_char[] = {'C','H','A','R',0};
374     static const WCHAR type_int[] = {'I','N','T',0};
375     static const WCHAR type_long[] = {'L','O','N','G',0};
376     static const WCHAR type_object[] = {'O','B','J','E','C','T',0};
377     static const WCHAR type_notnull[] = {' ','N','O','T',' ','N','U','L','L',0};
378     static const WCHAR localizable[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0};
379
380     columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
381     if (!columns)
382         return NULL;
383
384     for (i = 0; i < num_columns; i++)
385     {
386         type = NULL;
387         comma[1] = size[0] = extra[0] = '\0';
388
389         if (i == num_columns - 1)
390             comma[0] = '\0';
391         else
392             comma[0] = ',';
393
394         ptr = &types[i][1];
395         len = atolW(ptr);
396         extra[0] = '\0';
397
398         switch (types[i][0])
399         {
400             case 'l':
401                 lstrcpyW(extra, type_notnull);
402             case 'L':
403                 lstrcatW(extra, localizable);
404                 type = type_char;
405                 sprintfW(size, size_fmt, ptr);
406                 break;
407             case 's':
408                 lstrcpyW(extra, type_notnull);
409             case 'S':
410                 type = type_char;
411                 sprintfW(size, size_fmt, ptr);
412                 break;
413             case 'i':
414                 lstrcpyW(extra, type_notnull);
415             case 'I':
416                 if (len == 2)
417                     type = type_int;
418                 else
419                     type = type_long;
420                 break;
421             case 'v':
422                 lstrcpyW(extra, type_notnull);
423             case 'V':
424                 type = type_object;
425                 break;
426             default:
427                 ERR("Unknown type: %c\n", types[i][0]);
428                 msi_free(columns);
429                 return NULL;
430         }
431
432         sprintfW(expanded, column_fmt, columns_data[i], type, size, extra, comma);
433         sql_size += lstrlenW(expanded);
434
435         p = msi_realloc(columns, sql_size * sizeof(WCHAR));
436         if (!p)
437         {
438             msi_free(columns);
439             return NULL;
440         }
441         columns = p;
442
443         lstrcatW(columns, expanded);
444     }
445
446     return columns;
447 }
448
449 static LPWSTR msi_build_createsql_postlude(LPWSTR *primary_keys, DWORD num_keys)
450 {
451     LPWSTR postlude, keys, ptr;
452     DWORD size, key_size, i;
453
454     static const WCHAR key_fmt[] = {'`','%','s','`',',',' ',0};
455     static const WCHAR postlude_fmt[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0};
456
457     for (i = 0, size = 1; i < num_keys; i++)
458         size += lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) - 2;
459
460     keys = msi_alloc(size * sizeof(WCHAR));
461     if (!keys)
462         return NULL;
463
464     for (i = 0, ptr = keys; i < num_keys; i++)
465     {
466         key_size = lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) -2;
467         sprintfW(ptr, key_fmt, primary_keys[i]);
468         ptr += key_size;
469     }
470
471     /* remove final ', ' */
472     *(ptr - 2) = '\0';
473
474     size = lstrlenW(postlude_fmt) + size - 1;
475     postlude = msi_alloc(size * sizeof(WCHAR));
476     if (!postlude)
477         goto done;
478
479     sprintfW(postlude, postlude_fmt, keys);
480
481 done:
482     msi_free(keys);
483     return postlude;
484 }
485
486 static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_labels, DWORD num_columns)
487 {
488     UINT r;
489     DWORD size;
490     MSIQUERY *view;
491     LPWSTR create_sql;
492     LPWSTR prelude, columns_sql, postlude;
493
494     prelude = msi_build_createsql_prelude(labels[0]);
495     columns_sql = msi_build_createsql_columns(columns, types, num_columns);
496     postlude = msi_build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */
497
498     if (!prelude || !columns_sql || !postlude)
499         return ERROR_OUTOFMEMORY;
500
501     size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
502     create_sql = msi_alloc(size * sizeof(WCHAR));
503     if (!create_sql)
504         return ERROR_OUTOFMEMORY;
505
506     lstrcpyW(create_sql, prelude);
507     lstrcatW(create_sql, columns_sql);
508     lstrcatW(create_sql, postlude);
509
510     msi_free(prelude);
511     msi_free(columns_sql);
512     msi_free(postlude);
513
514     r = MSI_DatabaseOpenViewW( db, create_sql, &view );
515     msi_free(create_sql);
516
517     if (r != ERROR_SUCCESS)
518         return r;
519
520     r = MSI_ViewExecute(view, NULL);
521     MSI_ViewClose(view);
522     msiobj_release(&view->hdr);
523
524     return r;
525 }
526
527 static LPWSTR msi_import_stream_filename(LPCWSTR path, LPCWSTR name)
528 {
529     DWORD len;
530     LPWSTR fullname, ptr;
531
532     len = lstrlenW(path) + lstrlenW(name) + 1;
533     fullname = msi_alloc(len*sizeof(WCHAR));
534     if (!fullname)
535        return NULL;
536
537     lstrcpyW( fullname, path );
538
539     /* chop off extension from path */
540     ptr = strrchrW(fullname, '.');
541     if (!ptr)
542     {
543         msi_free (fullname);
544         return NULL;
545     }
546     *ptr++ = '\\';
547     lstrcpyW( ptr, name );
548     return fullname;
549 }
550
551 static UINT construct_record(DWORD num_columns, LPWSTR *types,
552                              LPWSTR *data, LPWSTR path, MSIRECORD **rec)
553 {
554     UINT i;
555
556     *rec = MSI_CreateRecord(num_columns);
557     if (!*rec)
558         return ERROR_OUTOFMEMORY;
559
560     for (i = 0; i < num_columns; i++)
561     {
562         switch (types[i][0])
563         {
564             case 'L': case 'l': case 'S': case 's':
565                 MSI_RecordSetStringW(*rec, i + 1, data[i]);
566                 break;
567             case 'I': case 'i':
568                 if (*data[i])
569                     MSI_RecordSetInteger(*rec, i + 1, atoiW(data[i]));
570                 break;
571             case 'V': case 'v':
572                 if (*data[i])
573                 {
574                     UINT r;
575                     LPWSTR file = msi_import_stream_filename(path, data[i]);
576                     if (!file)
577                         return ERROR_FUNCTION_FAILED;
578
579                     r = MSI_RecordSetStreamFromFileW(*rec, i + 1, file);
580                     msi_free (file);
581                     if (r != ERROR_SUCCESS)
582                         return ERROR_FUNCTION_FAILED;
583                 }
584                 break;
585             default:
586                 ERR("Unhandled column type: %c\n", types[i][0]);
587                 msiobj_release(&(*rec)->hdr);
588                 return ERROR_FUNCTION_FAILED;
589         }
590     }
591
592     return ERROR_SUCCESS;
593 }
594
595 static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types,
596                                      LPWSTR *labels, LPWSTR **records,
597                                      int num_columns, int num_records,
598                                      LPWSTR path)
599 {
600     UINT r;
601     int i;
602     MSIQUERY *view;
603     MSIRECORD *rec;
604
605     static const WCHAR select[] = {
606         'S','E','L','E','C','T',' ','*',' ',
607         'F','R','O','M',' ','`','%','s','`',0
608     };
609
610     r = MSI_OpenQuery(db, &view, select, labels[0]);
611     if (r != ERROR_SUCCESS)
612         return r;
613
614     while (MSI_ViewFetch(view, &rec) != ERROR_NO_MORE_ITEMS)
615     {
616         r = MSI_ViewModify(view, MSIMODIFY_DELETE, rec);
617         if (r != ERROR_SUCCESS)
618             goto done;
619     }
620
621     for (i = 0; i < num_records; i++)
622     {
623         r = construct_record(num_columns, types, records[i], path, &rec);
624         if (r != ERROR_SUCCESS)
625             goto done;
626
627         r = MSI_ViewModify(view, MSIMODIFY_INSERT, rec);
628         if (r != ERROR_SUCCESS)
629         {
630             msiobj_release(&rec->hdr);
631             goto done;
632         }
633
634         msiobj_release(&rec->hdr);
635     }
636
637 done:
638     msiobj_release(&view->hdr);
639     return r;
640 }
641
642 static UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
643 {
644     UINT r;
645     DWORD len, i;
646     DWORD num_labels, num_types;
647     DWORD num_columns, num_records = 0;
648     LPWSTR *columns, *types, *labels;
649     LPWSTR path, ptr, data;
650     LPWSTR **records = NULL;
651     LPWSTR **temp_records;
652
653     static const WCHAR backslash[] = {'\\',0};
654     static const WCHAR suminfo[] =
655         {'_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0};
656
657     TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
658
659     if( folder == NULL || file == NULL )
660         return ERROR_INVALID_PARAMETER;
661
662     len = lstrlenW(folder) + lstrlenW(backslash) + lstrlenW(file) + 1;
663     path = msi_alloc( len * sizeof(WCHAR) );
664     if (!path)
665         return ERROR_OUTOFMEMORY;
666
667     lstrcpyW( path, folder );
668     lstrcatW( path, backslash );
669     lstrcatW( path, file );
670
671     data = msi_read_text_archive( path );
672
673     ptr = data;
674     msi_parse_line( &ptr, &columns, &num_columns );
675     msi_parse_line( &ptr, &types, &num_types );
676     msi_parse_line( &ptr, &labels, &num_labels );
677
678     if (num_columns != num_types)
679     {
680         r = ERROR_FUNCTION_FAILED;
681         goto done;
682     }
683
684     records = msi_alloc(sizeof(LPWSTR *));
685     if (!records)
686     {
687         r = ERROR_OUTOFMEMORY;
688         goto done;
689     }
690
691     /* read in the table records */
692     while (*ptr)
693     {
694         msi_parse_line( &ptr, &records[num_records], NULL );
695
696         num_records++;
697         temp_records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *));
698         if (!temp_records)
699         {
700             r = ERROR_OUTOFMEMORY;
701             goto done;
702         }
703         records = temp_records;
704     }
705
706     if (!strcmpW(labels[0], suminfo))
707     {
708         r = msi_add_suminfo( db, records, num_records, num_columns );
709         if (r != ERROR_SUCCESS)
710         {
711             r = ERROR_FUNCTION_FAILED;
712             goto done;
713         }
714     }
715     else
716     {
717         if (!TABLE_Exists(db, labels[0]))
718         {
719             r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns );
720             if (r != ERROR_SUCCESS)
721             {
722                 r = ERROR_FUNCTION_FAILED;
723                 goto done;
724             }
725         }
726
727         r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records, path );
728     }
729
730 done:
731     msi_free(path);
732     msi_free(data);
733     msi_free(columns);
734     msi_free(types);
735     msi_free(labels);
736
737     for (i = 0; i < num_records; i++)
738         msi_free(records[i]);
739
740     msi_free(records);
741
742     return r;
743 }
744
745 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
746 {
747     MSIDATABASE *db;
748     UINT r;
749
750     TRACE("%x %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
751
752     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
753     if( !db )
754     {
755         IWineMsiRemoteDatabase *remote_database;
756
757         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
758         if ( !remote_database )
759             return ERROR_INVALID_HANDLE;
760
761         IWineMsiRemoteDatabase_Release( remote_database );
762         WARN("MsiDatabaseImport not allowed during a custom action!\n");
763
764         return ERROR_SUCCESS;
765     }
766
767     r = MSI_DatabaseImport( db, szFolder, szFilename );
768     msiobj_release( &db->hdr );
769     return r;
770 }
771
772 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
773                LPCSTR szFolder, LPCSTR szFilename )
774 {
775     LPWSTR path = NULL, file = NULL;
776     UINT r = ERROR_OUTOFMEMORY;
777
778     TRACE("%x %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
779
780     if( szFolder )
781     {
782         path = strdupAtoW( szFolder );
783         if( !path )
784             goto end;
785     }
786
787     if( szFilename )
788     {
789         file = strdupAtoW( szFilename );
790         if( !file )
791             goto end;
792     }
793
794     r = MsiDatabaseImportW( handle, path, file );
795
796 end:
797     msi_free( path );
798     msi_free( file );
799
800     return r;
801 }
802
803 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
804 {
805     UINT i, count, len, r = ERROR_SUCCESS;
806     const char *sep;
807     char *buffer;
808     DWORD sz;
809
810     len = 0x100;
811     buffer = msi_alloc( len );
812     if ( !buffer )
813         return ERROR_OUTOFMEMORY;
814
815     count = MSI_RecordGetFieldCount( row );
816     for ( i=start; i<=count; i++ )
817     {
818         sz = len;
819         r = MSI_RecordGetStringA( row, i, buffer, &sz );
820         if (r == ERROR_MORE_DATA)
821         {
822             char *p = msi_realloc( buffer, sz + 1 );
823             if (!p)
824                 break;
825             len = sz + 1;
826             buffer = p;
827         }
828         sz = len;
829         r = MSI_RecordGetStringA( row, i, buffer, &sz );
830         if (r != ERROR_SUCCESS)
831             break;
832
833         if (!WriteFile( handle, buffer, sz, &sz, NULL ))
834         {
835             r = ERROR_FUNCTION_FAILED;
836             break;
837         }
838
839         sep = (i < count) ? "\t" : "\r\n";
840         if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
841         {
842             r = ERROR_FUNCTION_FAILED;
843             break;
844         }
845     }
846     msi_free( buffer );
847     return r;
848 }
849
850 static UINT msi_export_row( MSIRECORD *row, void *arg )
851 {
852     return msi_export_record( arg, row, 1 );
853 }
854
855 static UINT msi_export_forcecodepage( HANDLE handle )
856 {
857     DWORD sz;
858
859     static const char data[] = "\r\n\r\n0\t_ForceCodepage\r\n";
860
861     FIXME("Read the codepage from the strings table!\n");
862
863     sz = lstrlenA(data) + 1;
864     if (!WriteFile(handle, data, sz, &sz, NULL))
865         return ERROR_FUNCTION_FAILED;
866
867     return ERROR_SUCCESS;
868 }
869
870 static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
871                LPCWSTR folder, LPCWSTR file )
872 {
873     static const WCHAR query[] = {
874         's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
875     static const WCHAR szbs[] = { '\\', 0 };
876     static const WCHAR forcecodepage[] = {
877         '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 };
878     MSIRECORD *rec = NULL;
879     MSIQUERY *view = NULL;
880     LPWSTR filename;
881     HANDLE handle;
882     UINT len, r;
883
884     TRACE("%p %s %s %s\n", db, debugstr_w(table),
885           debugstr_w(folder), debugstr_w(file) );
886
887     if( folder == NULL || file == NULL )
888         return ERROR_INVALID_PARAMETER;
889
890     len = lstrlenW(folder) + lstrlenW(file) + 2;
891     filename = msi_alloc(len * sizeof (WCHAR));
892     if (!filename)
893         return ERROR_OUTOFMEMORY;
894
895     lstrcpyW( filename, folder );
896     lstrcatW( filename, szbs );
897     lstrcatW( filename, file );
898
899     handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
900                           NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
901     msi_free( filename );
902     if (handle == INVALID_HANDLE_VALUE)
903         return ERROR_FUNCTION_FAILED;
904
905     if (!lstrcmpW( table, forcecodepage ))
906     {
907         r = msi_export_forcecodepage( handle );
908         goto done;
909     }
910
911     r = MSI_OpenQuery( db, &view, query, table );
912     if (r == ERROR_SUCCESS)
913     {
914         /* write out row 1, the column names */
915         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
916         if (r == ERROR_SUCCESS)
917         {
918             msi_export_record( handle, rec, 1 );
919             msiobj_release( &rec->hdr );
920         }
921
922         /* write out row 2, the column types */
923         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
924         if (r == ERROR_SUCCESS)
925         {
926             msi_export_record( handle, rec, 1 );
927             msiobj_release( &rec->hdr );
928         }
929
930         /* write out row 3, the table name + keys */
931         r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
932         if (r == ERROR_SUCCESS)
933         {
934             MSI_RecordSetStringW( rec, 0, table );
935             msi_export_record( handle, rec, 0 );
936             msiobj_release( &rec->hdr );
937         }
938
939         /* write out row 4 onwards, the data */
940         r = MSI_IterateRecords( view, 0, msi_export_row, handle );
941         msiobj_release( &view->hdr );
942     }
943
944 done:
945     CloseHandle( handle );
946     return r;
947 }
948
949 /***********************************************************************
950  * MsiExportDatabaseW        [MSI.@]
951  *
952  * Writes a file containing the table data as tab separated ASCII.
953  *
954  * The format is as follows:
955  *
956  * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
957  * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
958  * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
959  *
960  * Followed by the data, starting at row 1 with one row per line
961  *
962  * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
963  */
964 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
965                LPCWSTR szFolder, LPCWSTR szFilename )
966 {
967     MSIDATABASE *db;
968     UINT r;
969
970     TRACE("%x %s %s %s\n", handle, debugstr_w(szTable),
971           debugstr_w(szFolder), debugstr_w(szFilename));
972
973     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
974     if( !db )
975     {
976         IWineMsiRemoteDatabase *remote_database;
977
978         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
979         if ( !remote_database )
980             return ERROR_INVALID_HANDLE;
981
982         IWineMsiRemoteDatabase_Release( remote_database );
983         WARN("MsiDatabaseExport not allowed during a custom action!\n");
984
985         return ERROR_SUCCESS;
986     }
987
988     r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
989     msiobj_release( &db->hdr );
990     return r;
991 }
992
993 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
994                LPCSTR szFolder, LPCSTR szFilename )
995 {
996     LPWSTR path = NULL, file = NULL, table = NULL;
997     UINT r = ERROR_OUTOFMEMORY;
998
999     TRACE("%x %s %s %s\n", handle, debugstr_a(szTable),
1000           debugstr_a(szFolder), debugstr_a(szFilename));
1001
1002     if( szTable )
1003     {
1004         table = strdupAtoW( szTable );
1005         if( !table )
1006             goto end;
1007     }
1008
1009     if( szFolder )
1010     {
1011         path = strdupAtoW( szFolder );
1012         if( !path )
1013             goto end;
1014     }
1015
1016     if( szFilename )
1017     {
1018         file = strdupAtoW( szFilename );
1019         if( !file )
1020             goto end;
1021     }
1022
1023     r = MsiDatabaseExportW( handle, table, path, file );
1024
1025 end:
1026     msi_free( table );
1027     msi_free( path );
1028     msi_free( file );
1029
1030     return r;
1031 }
1032
1033 UINT WINAPI MsiDatabaseMergeA(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1034                               LPCSTR szTableName)
1035 {
1036     UINT r;
1037     LPWSTR table;
1038
1039     TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1040           debugstr_a(szTableName));
1041
1042     table = strdupAtoW(szTableName);
1043     r = MsiDatabaseMergeW(hDatabase, hDatabaseMerge, table);
1044
1045     msi_free(table);
1046     return r;
1047 }
1048
1049 typedef struct _tagMERGETABLE
1050 {
1051     struct list entry;
1052     struct list rows;
1053     LPWSTR name;
1054     DWORD numconflicts;
1055     LPWSTR *columns;
1056     DWORD numcolumns;
1057     LPWSTR *types;
1058     DWORD numtypes;
1059     LPWSTR *labels;
1060     DWORD numlabels;
1061 } MERGETABLE;
1062
1063 typedef struct _tagMERGEROW
1064 {
1065     struct list entry;
1066     MSIRECORD *data;
1067 } MERGEROW;
1068
1069 typedef struct _tagMERGEDATA
1070 {
1071     MSIDATABASE *db;
1072     MSIDATABASE *merge;
1073     MERGETABLE *curtable;
1074     MSIQUERY *curview;
1075     struct list *tabledata;
1076 } MERGEDATA;
1077
1078 static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview)
1079 {
1080     MSIRECORD *dbrec, *mergerec;
1081     UINT r, i, count;
1082
1083     r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec);
1084     if (r != ERROR_SUCCESS)
1085         return r;
1086
1087     r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec);
1088     if (r != ERROR_SUCCESS)
1089         return r;
1090
1091     count = MSI_RecordGetFieldCount(dbrec);
1092     for (i = 1; i <= count; i++)
1093     {
1094         if (!MSI_RecordGetString(mergerec, i))
1095             break;
1096
1097         if (lstrcmpW(MSI_RecordGetString(dbrec, i),
1098                      MSI_RecordGetString(mergerec, i)))
1099         {
1100             r = ERROR_DATATYPE_MISMATCH;
1101             goto done;
1102         }
1103     }
1104
1105     msiobj_release(&dbrec->hdr);
1106     msiobj_release(&mergerec->hdr);
1107     dbrec = mergerec = NULL;
1108
1109     r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec);
1110     if (r != ERROR_SUCCESS)
1111         return r;
1112
1113     r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec);
1114     if (r != ERROR_SUCCESS)
1115         return r;
1116
1117     count = MSI_RecordGetFieldCount(dbrec);
1118     for (i = 1; i <= count; i++)
1119     {
1120         if (!MSI_RecordGetString(mergerec, i))
1121             break;
1122
1123         if (lstrcmpW(MSI_RecordGetString(dbrec, i),
1124                      MSI_RecordGetString(mergerec, i)))
1125         {
1126             r = ERROR_DATATYPE_MISMATCH;
1127             break;
1128         }
1129     }
1130
1131 done:
1132     msiobj_release(&dbrec->hdr);
1133     msiobj_release(&mergerec->hdr);
1134
1135     return r;
1136 }
1137
1138 static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb,
1139                                       LPCWSTR table)
1140 {
1141     MSIRECORD *dbrec, *mergerec = NULL;
1142     UINT r, i, count;
1143
1144     r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec);
1145     if (r != ERROR_SUCCESS)
1146         return r;
1147
1148     r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec);
1149     if (r != ERROR_SUCCESS)
1150         goto done;
1151
1152     count = MSI_RecordGetFieldCount(dbrec);
1153     if (count != MSI_RecordGetFieldCount(mergerec))
1154     {
1155         r = ERROR_DATATYPE_MISMATCH;
1156         goto done;
1157     }
1158
1159     for (i = 1; i <= count; i++)
1160     {
1161         if (lstrcmpW(MSI_RecordGetString(dbrec, i),
1162                      MSI_RecordGetString(mergerec, i)))
1163         {
1164             r = ERROR_DATATYPE_MISMATCH;
1165             goto done;
1166         }
1167     }
1168
1169 done:
1170     msiobj_release(&dbrec->hdr);
1171     msiobj_release(&mergerec->hdr);
1172
1173     return r;
1174 }
1175
1176 static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec)
1177 {
1178     MSIRECORD *colnames;
1179     LPWSTR str, val;
1180     UINT r, i = 0, sz = 0;
1181     int cmp;
1182
1183     r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames);
1184     if (r != ERROR_SUCCESS)
1185         return NULL;
1186
1187     do
1188     {
1189         str = msi_dup_record_field(colnames, ++i);
1190         cmp = lstrcmpW(key, str);
1191         msi_free(str);
1192     } while (cmp);
1193
1194     msiobj_release(&colnames->hdr);
1195
1196     r = MSI_RecordGetStringW(rec, i, NULL, &sz);
1197     if (r != ERROR_SUCCESS)
1198         return NULL;
1199     sz++;
1200
1201     if (MSI_RecordGetString(rec, i))  /* check record field is a string */
1202     {
1203         /* quote string record fields */
1204         const WCHAR szQuote[] = {'\'', 0};
1205         sz += 2;
1206         val = msi_alloc(sz*sizeof(WCHAR));
1207         if (!val)
1208             return NULL;
1209
1210         lstrcpyW(val, szQuote);
1211         r = MSI_RecordGetStringW(rec, i, val+1, &sz);
1212         lstrcpyW(val+1+sz, szQuote);
1213     }
1214     else
1215     {
1216         /* do not quote integer record fields */
1217         val = msi_alloc(sz*sizeof(WCHAR));
1218         if (!val)
1219             return NULL;
1220
1221         r = MSI_RecordGetStringW(rec, i, val, &sz);
1222     }
1223
1224     if (r != ERROR_SUCCESS)
1225     {
1226         ERR("failed to get string!\n");
1227         msi_free(val);
1228         return NULL;
1229     }
1230
1231     return val;
1232 }
1233
1234 static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view,
1235                                     LPWSTR table, MSIRECORD *rec)
1236 {
1237     LPWSTR query = NULL, clause = NULL;
1238     LPWSTR ptr = NULL, val;
1239     LPCWSTR setptr;
1240     DWORD size = 1, oldsize;
1241     LPCWSTR key;
1242     MSIRECORD *keys;
1243     UINT r, i, count;
1244
1245     static const WCHAR keyset[] = {
1246         '`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0};
1247     static const WCHAR lastkeyset[] = {
1248         '`','%','s','`',' ','=',' ','%','s',' ',0};
1249     static const WCHAR fmt[] = {'S','E','L','E','C','T',' ','*',' ',
1250         'F','R','O','M',' ','`','%','s','`',' ',
1251         'W','H','E','R','E',' ','%','s',0};
1252
1253     r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys);
1254     if (r != ERROR_SUCCESS)
1255         return NULL;
1256
1257     clause = msi_alloc_zero(size * sizeof(WCHAR));
1258     if (!clause)
1259         goto done;
1260
1261     ptr = clause;
1262     count = MSI_RecordGetFieldCount(keys);
1263     for (i = 1; i <= count; i++)
1264     {
1265         key = MSI_RecordGetString(keys, i);
1266         val = get_key_value(view, key, rec);
1267
1268         if (i == count)
1269             setptr = lastkeyset;
1270         else
1271             setptr = keyset;
1272
1273         oldsize = size;
1274         size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4;
1275         clause = msi_realloc(clause, size * sizeof (WCHAR));
1276         if (!clause)
1277         {
1278             msi_free(val);
1279             goto done;
1280         }
1281
1282         ptr = clause + oldsize - 1;
1283         sprintfW(ptr, setptr, key, val);
1284         msi_free(val);
1285     }
1286
1287     size = lstrlenW(fmt) + lstrlenW(table) + lstrlenW(clause) + 1;
1288     query = msi_alloc(size * sizeof(WCHAR));
1289     if (!query)
1290         goto done;
1291
1292     sprintfW(query, fmt, table, clause);
1293
1294 done:
1295     msi_free(clause);
1296     msiobj_release(&keys->hdr);
1297     return query;
1298 }
1299
1300 static UINT merge_diff_row(MSIRECORD *rec, LPVOID param)
1301 {
1302     MERGEDATA *data = param;
1303     MERGETABLE *table = data->curtable;
1304     MERGEROW *mergerow;
1305     MSIQUERY *dbview = NULL;
1306     MSIRECORD *row = NULL;
1307     LPWSTR query = NULL;
1308     UINT r = ERROR_SUCCESS;
1309
1310     if (TABLE_Exists(data->db, table->name))
1311     {
1312         query = create_diff_row_query(data->merge, data->curview, table->name, rec);
1313         if (!query)
1314             return ERROR_OUTOFMEMORY;
1315
1316         r = MSI_DatabaseOpenViewW(data->db, query, &dbview);
1317         if (r != ERROR_SUCCESS)
1318             goto done;
1319
1320         r = MSI_ViewExecute(dbview, NULL);
1321         if (r != ERROR_SUCCESS)
1322             goto done;
1323
1324         r = MSI_ViewFetch(dbview, &row);
1325         if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row))
1326         {
1327             table->numconflicts++;
1328             goto done;
1329         }
1330         else if (r != ERROR_NO_MORE_ITEMS)
1331             goto done;
1332     }
1333
1334     mergerow = msi_alloc(sizeof(MERGEROW));
1335     if (!mergerow)
1336     {
1337         r = ERROR_OUTOFMEMORY;
1338         goto done;
1339     }
1340
1341     mergerow->data = MSI_CloneRecord(rec);
1342     if (!mergerow->data)
1343     {
1344         r = ERROR_OUTOFMEMORY;
1345         msi_free(mergerow);
1346         goto done;
1347     }
1348
1349     list_add_tail(&table->rows, &mergerow->entry);
1350
1351 done:
1352     msi_free(query);
1353     msiobj_release(&row->hdr);
1354     msiobj_release(&dbview->hdr);
1355     return r;
1356 }
1357
1358 static UINT msi_get_table_labels(MSIDATABASE *db, LPCWSTR table, LPWSTR **labels, DWORD *numlabels)
1359 {
1360     UINT r, i, count;
1361     MSIRECORD *prec = NULL;
1362
1363     r = MSI_DatabaseGetPrimaryKeys(db, table, &prec);
1364     if (r != ERROR_SUCCESS)
1365         return r;
1366
1367     count = MSI_RecordGetFieldCount(prec);
1368     *numlabels = count + 1;
1369     *labels = msi_alloc((*numlabels)*sizeof(LPWSTR));
1370     if (!*labels)
1371     {
1372         r = ERROR_OUTOFMEMORY;
1373         goto end;
1374     }
1375
1376     (*labels)[0] = strdupW(table);
1377     for (i=1; i<=count; i++ )
1378     {
1379         (*labels)[i] = strdupW(MSI_RecordGetString(prec, i));
1380     }
1381
1382 end:
1383     msiobj_release( &prec->hdr );
1384     return r;
1385 }
1386
1387 static UINT msi_get_query_columns(MSIQUERY *query, LPWSTR **columns, DWORD *numcolumns)
1388 {
1389     UINT r, i, count;
1390     MSIRECORD *prec = NULL;
1391
1392     r = MSI_ViewGetColumnInfo(query, MSICOLINFO_NAMES, &prec);
1393     if (r != ERROR_SUCCESS)
1394         return r;
1395
1396     count = MSI_RecordGetFieldCount(prec);
1397     *columns = msi_alloc(count*sizeof(LPWSTR));
1398     if (!*columns)
1399     {
1400         r = ERROR_OUTOFMEMORY;
1401         goto end;
1402     }
1403
1404     for (i=1; i<=count; i++ )
1405     {
1406         (*columns)[i-1] = strdupW(MSI_RecordGetString(prec, i));
1407     }
1408
1409     *numcolumns = count;
1410
1411 end:
1412     msiobj_release( &prec->hdr );
1413     return r;
1414 }
1415
1416 static UINT msi_get_query_types(MSIQUERY *query, LPWSTR **types, DWORD *numtypes)
1417 {
1418     UINT r, i, count;
1419     MSIRECORD *prec = NULL;
1420
1421     r = MSI_ViewGetColumnInfo(query, MSICOLINFO_TYPES, &prec);
1422     if (r != ERROR_SUCCESS)
1423         return r;
1424
1425     count = MSI_RecordGetFieldCount(prec);
1426     *types = msi_alloc(count*sizeof(LPWSTR));
1427     if (!*types)
1428     {
1429         r = ERROR_OUTOFMEMORY;
1430         goto end;
1431     }
1432
1433     for (i=1; i<=count; i++ )
1434     {
1435         (*types)[i-1] = strdupW(MSI_RecordGetString(prec, i));
1436     }
1437
1438 end:
1439     msiobj_release( &prec->hdr );
1440     return r;
1441 }
1442
1443 static void merge_free_rows(MERGETABLE *table)
1444 {
1445     struct list *item, *cursor;
1446
1447     LIST_FOR_EACH_SAFE(item, cursor, &table->rows)
1448     {
1449         MERGEROW *row = LIST_ENTRY(item, MERGEROW, entry);
1450
1451         list_remove(&row->entry);
1452         msiobj_release(&row->data->hdr);
1453         msi_free(row);
1454     }
1455 }
1456
1457 static void free_merge_table(MERGETABLE *table)
1458 {
1459         UINT i;
1460
1461         if (table->labels != NULL)
1462         {
1463             for (i = 0; i < table->numlabels; i++)
1464                 msi_free(table->labels[i]);
1465             msi_free(table->labels);
1466         }
1467
1468         if (table->columns != NULL)
1469         {
1470             for (i = 0; i < table->numcolumns; i++)
1471                 msi_free(table->columns[i]);
1472             msi_free(table->columns);
1473         }
1474
1475         if (table->types != NULL)
1476         {
1477             for (i = 0; i < table->numtypes; i++)
1478                 msi_free(table->types[i]);
1479             msi_free(table->types);
1480         }
1481         msi_free(table->name);
1482         merge_free_rows(table);
1483
1484         msi_free(table);
1485 }
1486
1487 static UINT msi_get_merge_table (MSIDATABASE *db, LPCWSTR name, MERGETABLE **ptable)
1488 {
1489     UINT r;
1490     MERGETABLE *table;
1491     MSIQUERY *mergeview = NULL;
1492
1493     static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1494         'F','R','O','M',' ','`','%','s','`',0};
1495
1496     table = msi_alloc_zero(sizeof(MERGETABLE));
1497     if (!table)
1498     {
1499        *ptable = NULL;
1500        return ERROR_OUTOFMEMORY;
1501     }
1502
1503     r = msi_get_table_labels(db, name, &table->labels, &table->numlabels);
1504     if (r != ERROR_SUCCESS)
1505         goto err;
1506
1507     r = MSI_OpenQuery(db, &mergeview, query, name);
1508     if (r != ERROR_SUCCESS)
1509         goto err;
1510
1511     r = msi_get_query_columns(mergeview, &table->columns, &table->numcolumns);
1512     if (r != ERROR_SUCCESS)
1513         goto err;
1514
1515     r = msi_get_query_types(mergeview, &table->types, &table->numtypes);
1516     if (r != ERROR_SUCCESS)
1517         goto err;
1518
1519     list_init(&table->rows);
1520
1521     table->name = strdupW(name);
1522     table->numconflicts = 0;
1523
1524     msiobj_release(&mergeview->hdr);
1525     *ptable = table;
1526     return ERROR_SUCCESS;
1527
1528 err:
1529     msiobj_release(&mergeview->hdr);
1530     free_merge_table(table);
1531     *ptable = NULL;
1532     return r;
1533 }
1534
1535 static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
1536 {
1537     MERGEDATA *data = param;
1538     MERGETABLE *table;
1539     MSIQUERY *dbview = NULL;
1540     MSIQUERY *mergeview = NULL;
1541     LPCWSTR name;
1542     UINT r;
1543
1544     static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1545         'F','R','O','M',' ','`','%','s','`',0};
1546
1547     name = MSI_RecordGetString(rec, 1);
1548
1549     r = MSI_OpenQuery(data->merge, &mergeview, query, name);
1550     if (r != ERROR_SUCCESS)
1551         goto done;
1552
1553     if (TABLE_Exists(data->db, name))
1554     {
1555         r = MSI_OpenQuery(data->db, &dbview, query, name);
1556         if (r != ERROR_SUCCESS)
1557             goto done;
1558
1559         r = merge_verify_colnames(dbview, mergeview);
1560         if (r != ERROR_SUCCESS)
1561             goto done;
1562
1563         r = merge_verify_primary_keys(data->db, data->merge, name);
1564         if (r != ERROR_SUCCESS)
1565             goto done;
1566     }
1567
1568     r = msi_get_merge_table(data->merge, name, &table);
1569     if (r != ERROR_SUCCESS)
1570         goto done;
1571
1572     data->curtable = table;
1573     data->curview = mergeview;
1574     r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
1575     if (r != ERROR_SUCCESS)
1576     {
1577         free_merge_table(table);
1578         goto done;
1579     }
1580
1581     list_add_tail(data->tabledata, &table->entry);
1582
1583 done:
1584     msiobj_release(&dbview->hdr);
1585     msiobj_release(&mergeview->hdr);
1586     return r;
1587 }
1588
1589 static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge,
1590                               struct list *tabledata)
1591 {
1592     UINT r;
1593     MSIQUERY *view;
1594     MERGEDATA data;
1595
1596     static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1597         'F','R','O','M',' ','`','_','T','a','b','l','e','s','`',0};
1598
1599     r = MSI_DatabaseOpenViewW(merge, query, &view);
1600     if (r != ERROR_SUCCESS)
1601         return r;
1602
1603     data.db = db;
1604     data.merge = merge;
1605     data.tabledata = tabledata;
1606     r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data);
1607
1608     msiobj_release(&view->hdr);
1609     return r;
1610 }
1611
1612 static UINT merge_table(MSIDATABASE *db, MERGETABLE *table)
1613 {
1614     UINT r;
1615     MERGEROW *row;
1616     MSIVIEW *tv;
1617
1618     if (!TABLE_Exists(db, table->name))
1619     {
1620         r = msi_add_table_to_db(db, table->columns, table->types,
1621                table->labels, table->numlabels, table->numcolumns);
1622         if (r != ERROR_SUCCESS)
1623            return ERROR_FUNCTION_FAILED;
1624     }
1625
1626     LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry)
1627     {
1628         r = TABLE_CreateView(db, table->name, &tv);
1629         if (r != ERROR_SUCCESS)
1630             return r;
1631
1632         r = tv->ops->insert_row(tv, row->data, -1, FALSE);
1633         tv->ops->delete(tv);
1634
1635         if (r != ERROR_SUCCESS)
1636             return r;
1637     }
1638
1639     return ERROR_SUCCESS;
1640 }
1641
1642 static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error,
1643                                 LPWSTR table, DWORD numconflicts)
1644 {
1645     UINT r;
1646     MSIQUERY *view;
1647
1648     static const WCHAR create[] = {
1649         'C','R','E','A','T','E',' ','T','A','B','L','E',' ',
1650         '`','%','s','`',' ','(','`','T','a','b','l','e','`',' ',
1651         'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ',
1652         'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e',
1653         'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ',
1654         'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ',
1655         'K','E','Y',' ','`','T','a','b','l','e','`',')',0};
1656     static const WCHAR insert[] = {
1657         'I','N','S','E','R','T',' ','I','N','T','O',' ',
1658         '`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ',
1659         '`','N','u','m','R','o','w','M','e','r','g','e',
1660         'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S',
1661         ' ','(','\'','%','s','\'',',',' ','%','d',')',0};
1662
1663     if (!TABLE_Exists(db, error))
1664     {
1665         r = MSI_OpenQuery(db, &view, create, error);
1666         if (r != ERROR_SUCCESS)
1667             return r;
1668
1669         r = MSI_ViewExecute(view, NULL);
1670         msiobj_release(&view->hdr);
1671         if (r != ERROR_SUCCESS)
1672             return r;
1673     }
1674
1675     r = MSI_OpenQuery(db, &view, insert, error, table, numconflicts);
1676     if (r != ERROR_SUCCESS)
1677         return r;
1678
1679     r = MSI_ViewExecute(view, NULL);
1680     msiobj_release(&view->hdr);
1681     return r;
1682 }
1683
1684 UINT WINAPI MsiDatabaseMergeW(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1685                               LPCWSTR szTableName)
1686 {
1687     struct list tabledata = LIST_INIT(tabledata);
1688     struct list *item, *cursor;
1689     MSIDATABASE *db, *merge;
1690     MERGETABLE *table;
1691     BOOL conflicts;
1692     UINT r;
1693
1694     TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1695           debugstr_w(szTableName));
1696
1697     if (szTableName && !*szTableName)
1698         return ERROR_INVALID_TABLE;
1699
1700     db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE);
1701     merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE);
1702     if (!db || !merge)
1703     {
1704         r = ERROR_INVALID_HANDLE;
1705         goto done;
1706     }
1707
1708     r = gather_merge_data(db, merge, &tabledata);
1709     if (r != ERROR_SUCCESS)
1710         goto done;
1711
1712     conflicts = FALSE;
1713     LIST_FOR_EACH_ENTRY(table, &tabledata, MERGETABLE, entry)
1714     {
1715         if (table->numconflicts)
1716         {
1717             conflicts = TRUE;
1718
1719             r = update_merge_errors(db, szTableName, table->name,
1720                                     table->numconflicts);
1721             if (r != ERROR_SUCCESS)
1722                 break;
1723         }
1724         else
1725         {
1726             r = merge_table(db, table);
1727             if (r != ERROR_SUCCESS)
1728                 break;
1729         }
1730     }
1731
1732     LIST_FOR_EACH_SAFE(item, cursor, &tabledata)
1733     {
1734         MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry);
1735
1736         list_remove(&table->entry);
1737         free_merge_table(table);
1738     }
1739
1740     if (conflicts)
1741         r = ERROR_FUNCTION_FAILED;
1742
1743 done:
1744     msiobj_release(&db->hdr);
1745     msiobj_release(&merge->hdr);
1746     return r;
1747 }
1748
1749 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
1750 {
1751     MSIDBSTATE ret = MSIDBSTATE_READ;
1752     MSIDATABASE *db;
1753
1754     TRACE("%d\n", handle);
1755
1756     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
1757     if( !db )
1758     {
1759         IWineMsiRemoteDatabase *remote_database;
1760
1761         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
1762         if ( !remote_database )
1763             return MSIDBSTATE_ERROR;
1764
1765         IWineMsiRemoteDatabase_Release( remote_database );
1766         WARN("MsiGetDatabaseState not allowed during a custom action!\n");
1767
1768         return MSIDBSTATE_READ;
1769     }
1770
1771     if (db->mode != MSIDBOPEN_READONLY )
1772         ret = MSIDBSTATE_WRITE;
1773     msiobj_release( &db->hdr );
1774
1775     return ret;
1776 }
1777
1778 typedef struct _msi_remote_database_impl {
1779     const IWineMsiRemoteDatabaseVtbl *lpVtbl;
1780     MSIHANDLE database;
1781     LONG refs;
1782 } msi_remote_database_impl;
1783
1784 static inline msi_remote_database_impl* mrd_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase* iface )
1785 {
1786     return (msi_remote_database_impl *)iface;
1787 }
1788
1789 static HRESULT WINAPI mrd_QueryInterface( IWineMsiRemoteDatabase *iface,
1790                                           REFIID riid,LPVOID *ppobj)
1791 {
1792     if( IsEqualCLSID( riid, &IID_IUnknown ) ||
1793         IsEqualCLSID( riid, &IID_IWineMsiRemoteDatabase ) )
1794     {
1795         IUnknown_AddRef( iface );
1796         *ppobj = iface;
1797         return S_OK;
1798     }
1799
1800     return E_NOINTERFACE;
1801 }
1802
1803 static ULONG WINAPI mrd_AddRef( IWineMsiRemoteDatabase *iface )
1804 {
1805     msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
1806
1807     return InterlockedIncrement( &This->refs );
1808 }
1809
1810 static ULONG WINAPI mrd_Release( IWineMsiRemoteDatabase *iface )
1811 {
1812     msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
1813     ULONG r;
1814
1815     r = InterlockedDecrement( &This->refs );
1816     if (r == 0)
1817     {
1818         MsiCloseHandle( This->database );
1819         msi_free( This );
1820     }
1821     return r;
1822 }
1823
1824 static HRESULT WINAPI mrd_IsTablePersistent( IWineMsiRemoteDatabase *iface,
1825                                              BSTR table, MSICONDITION *persistent )
1826 {
1827     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
1828     *persistent = MsiDatabaseIsTablePersistentW(This->database, table);
1829     return S_OK;
1830 }
1831
1832 static HRESULT WINAPI mrd_GetPrimaryKeys( IWineMsiRemoteDatabase *iface,
1833                                           BSTR table, MSIHANDLE *keys )
1834 {
1835     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
1836     UINT r = MsiDatabaseGetPrimaryKeysW(This->database, table, keys);
1837     return HRESULT_FROM_WIN32(r);
1838 }
1839
1840 static HRESULT WINAPI mrd_GetSummaryInformation( IWineMsiRemoteDatabase *iface,
1841                                                 UINT updatecount, MSIHANDLE *suminfo )
1842 {
1843     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
1844     UINT r = MsiGetSummaryInformationW(This->database, NULL, updatecount, suminfo);
1845     return HRESULT_FROM_WIN32(r);
1846 }
1847
1848 static HRESULT WINAPI mrd_OpenView( IWineMsiRemoteDatabase *iface,
1849                                     BSTR query, MSIHANDLE *view )
1850 {
1851     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
1852     UINT r = MsiDatabaseOpenViewW(This->database, query, view);
1853     return HRESULT_FROM_WIN32(r);
1854 }
1855
1856 static HRESULT WINAPI mrd_SetMsiHandle( IWineMsiRemoteDatabase *iface, MSIHANDLE handle )
1857 {
1858     msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
1859     This->database = handle;
1860     return S_OK;
1861 }
1862
1863 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl =
1864 {
1865     mrd_QueryInterface,
1866     mrd_AddRef,
1867     mrd_Release,
1868     mrd_IsTablePersistent,
1869     mrd_GetPrimaryKeys,
1870     mrd_GetSummaryInformation,
1871     mrd_OpenView,
1872     mrd_SetMsiHandle,
1873 };
1874
1875 HRESULT create_msi_remote_database( IUnknown *pOuter, LPVOID *ppObj )
1876 {
1877     msi_remote_database_impl *This;
1878
1879     This = msi_alloc( sizeof *This );
1880     if (!This)
1881         return E_OUTOFMEMORY;
1882
1883     This->lpVtbl = &msi_remote_database_vtbl;
1884     This->database = 0;
1885     This->refs = 1;
1886
1887     *ppObj = This;
1888
1889     return S_OK;
1890 }