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