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