[MSI]
[reactos.git] / reactos / dll / win32 / 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 }