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