a94cebdbdfaee389e619d04f76f9f8cbf20d39cc
[reactos.git] / 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, 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 ptr += sprintfW(ptr, key_fmt, primary_keys[i]);
567 }
568
569 /* remove final ', ' */
570 *(ptr - 2) = '\0';
571
572 size = lstrlenW(postlude_fmt) + size - 1;
573 postlude = msi_alloc(size * sizeof(WCHAR));
574 if (!postlude)
575 goto done;
576
577 sprintfW(postlude, postlude_fmt, keys);
578
579 done:
580 msi_free(keys);
581 return postlude;
582 }
583
584 static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_labels, DWORD num_columns)
585 {
586 UINT r = ERROR_OUTOFMEMORY;
587 DWORD size;
588 MSIQUERY *view;
589 LPWSTR create_sql = NULL;
590 LPWSTR prelude, columns_sql, postlude;
591
592 prelude = msi_build_createsql_prelude(labels[0]);
593 columns_sql = msi_build_createsql_columns(columns, types, num_columns);
594 postlude = msi_build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */
595
596 if (!prelude || !columns_sql || !postlude)
597 goto done;
598
599 size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
600 create_sql = msi_alloc(size * sizeof(WCHAR));
601 if (!create_sql)
602 goto done;
603
604 lstrcpyW(create_sql, prelude);
605 lstrcatW(create_sql, columns_sql);
606 lstrcatW(create_sql, postlude);
607
608 r = MSI_DatabaseOpenViewW( db, create_sql, &view );
609 if (r != ERROR_SUCCESS)
610 goto done;
611
612 r = MSI_ViewExecute(view, NULL);
613 MSI_ViewClose(view);
614 msiobj_release(&view->hdr);
615
616 done:
617 msi_free(prelude);
618 msi_free(columns_sql);
619 msi_free(postlude);
620 msi_free(create_sql);
621 return r;
622 }
623
624 static LPWSTR msi_import_stream_filename(LPCWSTR path, LPCWSTR name)
625 {
626 DWORD len;
627 LPWSTR fullname, ptr;
628
629 len = lstrlenW(path) + lstrlenW(name) + 1;
630 fullname = msi_alloc(len*sizeof(WCHAR));
631 if (!fullname)
632 return NULL;
633
634 lstrcpyW( fullname, path );
635
636 /* chop off extension from path */
637 ptr = strrchrW(fullname, '.');
638 if (!ptr)
639 {
640 msi_free (fullname);
641 return NULL;
642 }
643 *ptr++ = '\\';
644 lstrcpyW( ptr, name );
645 return fullname;
646 }
647
648 static UINT construct_record(DWORD num_columns, LPWSTR *types,
649 LPWSTR *data, LPWSTR path, MSIRECORD **rec)
650 {
651 UINT i;
652
653 *rec = MSI_CreateRecord(num_columns);
654 if (!*rec)
655 return ERROR_OUTOFMEMORY;
656
657 for (i = 0; i < num_columns; i++)
658 {
659 switch (types[i][0])
660 {
661 case 'L': case 'l': case 'S': case 's':
662 MSI_RecordSetStringW(*rec, i + 1, data[i]);
663 break;
664 case 'I': case 'i':
665 if (*data[i])
666 MSI_RecordSetInteger(*rec, i + 1, atoiW(data[i]));
667 break;
668 case 'V': case 'v':
669 if (*data[i])
670 {
671 UINT r;
672 LPWSTR file = msi_import_stream_filename(path, data[i]);
673 if (!file)
674 return ERROR_FUNCTION_FAILED;
675
676 r = MSI_RecordSetStreamFromFileW(*rec, i + 1, file);
677 msi_free (file);
678 if (r != ERROR_SUCCESS)
679 return ERROR_FUNCTION_FAILED;
680 }
681 break;
682 default:
683 ERR("Unhandled column type: %c\n", types[i][0]);
684 msiobj_release(&(*rec)->hdr);
685 return ERROR_FUNCTION_FAILED;
686 }
687 }
688
689 return ERROR_SUCCESS;
690 }
691
692 static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types,
693 LPWSTR *labels, LPWSTR **records,
694 int num_columns, int num_records,
695 LPWSTR path)
696 {
697 UINT r;
698 int i;
699 MSIQUERY *view;
700 MSIRECORD *rec;
701
702 static const WCHAR select[] = {
703 'S','E','L','E','C','T',' ','*',' ',
704 'F','R','O','M',' ','`','%','s','`',0
705 };
706
707 r = MSI_OpenQuery(db, &view, select, labels[0]);
708 if (r != ERROR_SUCCESS)
709 return r;
710
711 while (MSI_ViewFetch(view, &rec) != ERROR_NO_MORE_ITEMS)
712 {
713 r = MSI_ViewModify(view, MSIMODIFY_DELETE, rec);
714 msiobj_release(&rec->hdr);
715 if (r != ERROR_SUCCESS)
716 goto done;
717 }
718
719 for (i = 0; i < num_records; i++)
720 {
721 r = construct_record(num_columns, types, records[i], path, &rec);
722 if (r != ERROR_SUCCESS)
723 goto done;
724
725 r = MSI_ViewModify(view, MSIMODIFY_INSERT, rec);
726 if (r != ERROR_SUCCESS)
727 {
728 msiobj_release(&rec->hdr);
729 goto done;
730 }
731
732 msiobj_release(&rec->hdr);
733 }
734
735 done:
736 msiobj_release(&view->hdr);
737 return r;
738 }
739
740 static UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
741 {
742 UINT r;
743 DWORD len, i;
744 DWORD num_labels, num_types;
745 DWORD num_columns, num_records = 0;
746 LPWSTR *columns, *types, *labels;
747 LPWSTR path, ptr, data;
748 LPWSTR **records = NULL;
749 LPWSTR **temp_records;
750
751 static const WCHAR suminfo[] =
752 {'_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0};
753 static const WCHAR forcecodepage[] =
754 {'_','F','o','r','c','e','C','o','d','e','p','a','g','e',0};
755
756 TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
757
758 if( folder == NULL || file == NULL )
759 return ERROR_INVALID_PARAMETER;
760
761 len = lstrlenW(folder) + lstrlenW(szBackSlash) + lstrlenW(file) + 1;
762 path = msi_alloc( len * sizeof(WCHAR) );
763 if (!path)
764 return ERROR_OUTOFMEMORY;
765
766 lstrcpyW( path, folder );
767 lstrcatW( path, szBackSlash );
768 lstrcatW( path, file );
769
770 data = msi_read_text_archive( path, &len );
771 if (!data)
772 {
773 msi_free(path);
774 return ERROR_FUNCTION_FAILED;
775 }
776
777 ptr = data;
778 msi_parse_line( &ptr, &columns, &num_columns, &len );
779 msi_parse_line( &ptr, &types, &num_types, &len );
780 msi_parse_line( &ptr, &labels, &num_labels, &len );
781
782 if (num_columns == 1 && !columns[0][0] && num_labels == 1 && !labels[0][0] &&
783 num_types == 2 && !strcmpW( types[1], forcecodepage ))
784 {
785 r = msi_set_string_table_codepage( db->strings, atoiW( types[0] ) );
786 goto done;
787 }
788
789 if (num_columns != num_types)
790 {
791 r = ERROR_FUNCTION_FAILED;
792 goto done;
793 }
794
795 records = msi_alloc(sizeof(LPWSTR *));
796 if (!records)
797 {
798 r = ERROR_OUTOFMEMORY;
799 goto done;
800 }
801
802 /* read in the table records */
803 while (len)
804 {
805 msi_parse_line( &ptr, &records[num_records], NULL, &len );
806
807 num_records++;
808 temp_records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *));
809 if (!temp_records)
810 {
811 r = ERROR_OUTOFMEMORY;
812 goto done;
813 }
814 records = temp_records;
815 }
816
817 if (!strcmpW(labels[0], suminfo))
818 {
819 r = msi_add_suminfo( db, records, num_records, num_columns );
820 if (r != ERROR_SUCCESS)
821 {
822 r = ERROR_FUNCTION_FAILED;
823 goto done;
824 }
825 }
826 else
827 {
828 if (!TABLE_Exists(db, labels[0]))
829 {
830 r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns );
831 if (r != ERROR_SUCCESS)
832 {
833 r = ERROR_FUNCTION_FAILED;
834 goto done;
835 }
836 }
837
838 r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records, path );
839 }
840
841 done:
842 msi_free(path);
843 msi_free(data);
844 msi_free(columns);
845 msi_free(types);
846 msi_free(labels);
847
848 for (i = 0; i < num_records; i++)
849 msi_free(records[i]);
850
851 msi_free(records);
852
853 return r;
854 }
855
856 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
857 {
858 MSIDATABASE *db;
859 UINT r;
860
861 TRACE("%x %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
862
863 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
864 if( !db )
865 {
866 IWineMsiRemoteDatabase *remote_database;
867
868 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
869 if ( !remote_database )
870 return ERROR_INVALID_HANDLE;
871
872 IWineMsiRemoteDatabase_Release( remote_database );
873 WARN("MsiDatabaseImport not allowed during a custom action!\n");
874
875 return ERROR_SUCCESS;
876 }
877
878 r = MSI_DatabaseImport( db, szFolder, szFilename );
879 msiobj_release( &db->hdr );
880 return r;
881 }
882
883 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
884 LPCSTR szFolder, LPCSTR szFilename )
885 {
886 LPWSTR path = NULL, file = NULL;
887 UINT r = ERROR_OUTOFMEMORY;
888
889 TRACE("%x %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
890
891 if( szFolder )
892 {
893 path = strdupAtoW( szFolder );
894 if( !path )
895 goto end;
896 }
897
898 if( szFilename )
899 {
900 file = strdupAtoW( szFilename );
901 if( !file )
902 goto end;
903 }
904
905 r = MsiDatabaseImportW( handle, path, file );
906
907 end:
908 msi_free( path );
909 msi_free( file );
910
911 return r;
912 }
913
914 static UINT msi_export_field( HANDLE handle, MSIRECORD *row, UINT field )
915 {
916 char *buffer;
917 BOOL bret;
918 DWORD sz;
919 UINT r;
920
921 sz = 0x100;
922 buffer = msi_alloc( sz );
923 if (!buffer)
924 return ERROR_OUTOFMEMORY;
925
926 r = MSI_RecordGetStringA( row, field, buffer, &sz );
927 if (r == ERROR_MORE_DATA)
928 {
929 char *p;
930
931 sz++; /* leave room for NULL terminator */
932 p = msi_realloc( buffer, sz );
933 if (!p)
934 {
935 msi_free( buffer );
936 return ERROR_OUTOFMEMORY;
937 }
938 buffer = p;
939
940 r = MSI_RecordGetStringA( row, field, buffer, &sz );
941 if (r != ERROR_SUCCESS)
942 {
943 msi_free( buffer );
944 return r;
945 }
946 }
947 else if (r != ERROR_SUCCESS)
948 return r;
949
950 bret = WriteFile( handle, buffer, sz, &sz, NULL );
951 msi_free( buffer );
952 if (!bret)
953 return ERROR_FUNCTION_FAILED;
954
955 return r;
956 }
957
958 static UINT msi_export_stream( LPCWSTR folder, LPCWSTR table, MSIRECORD *row, UINT field,
959 UINT start )
960 {
961 static const WCHAR fmt_file[] = { '%','s','/','%','s','/','%','s',0 };
962 static const WCHAR fmt_folder[] = { '%','s','/','%','s',0 };
963 WCHAR stream_name[256], stream_filename[MAX_PATH];
964 DWORD sz, read_size, write_size;
965 char buffer[1024];
966 HANDLE file;
967 UINT r;
968
969 /* get the name of the file */
970 sz = sizeof(stream_name)/sizeof(WCHAR);
971 r = MSI_RecordGetStringW( row, start, stream_name, &sz );
972 if (r != ERROR_SUCCESS)
973 return r;
974
975 /* if the destination folder does not exist then create it (folder name = table name) */
976 snprintfW( stream_filename, sizeof(stream_filename)/sizeof(WCHAR), fmt_folder, folder, table );
977 if (GetFileAttributesW( stream_filename ) == INVALID_FILE_ATTRIBUTES)
978 {
979 if (!CreateDirectoryW( stream_filename, NULL ))
980 return ERROR_PATH_NOT_FOUND;
981 }
982
983 /* actually create the file */
984 snprintfW( stream_filename, sizeof(stream_filename)/sizeof(WCHAR), fmt_file, folder, table, stream_name );
985 file = CreateFileW( stream_filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
986 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
987 if (file == INVALID_HANDLE_VALUE)
988 return ERROR_FILE_NOT_FOUND;
989
990 /* copy the stream to the file */
991 read_size = sizeof(buffer);
992 while (read_size == sizeof(buffer))
993 {
994 r = MSI_RecordReadStream( row, field, buffer, &read_size );
995 if (r != ERROR_SUCCESS)
996 {
997 CloseHandle( file );
998 return r;
999 }
1000 if (!WriteFile( file, buffer, read_size, &write_size, NULL ) || read_size != write_size)
1001 {
1002 CloseHandle( file );
1003 return ERROR_WRITE_FAULT;
1004 }
1005 }
1006 CloseHandle( file );
1007 return r;
1008 }
1009
1010 static UINT msi_export_record( struct row_export_info *row_export_info, MSIRECORD *row, UINT start )
1011 {
1012 HANDLE handle = row_export_info->handle;
1013 UINT i, count, r = ERROR_SUCCESS;
1014 const char *sep;
1015 DWORD sz;
1016
1017 count = MSI_RecordGetFieldCount( row );
1018 for (i = start; i <= count; i++)
1019 {
1020 r = msi_export_field( handle, row, i );
1021 if (r == ERROR_INVALID_PARAMETER)
1022 {
1023 r = msi_export_stream( row_export_info->folder, row_export_info->table, row, i, start );
1024 if (r != ERROR_SUCCESS)
1025 return r;
1026
1027 /* exporting a binary stream, repeat the "Name" field */
1028 r = msi_export_field( handle, row, start );
1029 if (r != ERROR_SUCCESS)
1030 return r;
1031 }
1032 else if (r != ERROR_SUCCESS)
1033 return r;
1034
1035 sep = (i < count) ? "\t" : "\r\n";
1036 if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
1037 return ERROR_FUNCTION_FAILED;
1038 }
1039 return r;
1040 }
1041
1042 static UINT msi_export_row( MSIRECORD *row, void *arg )
1043 {
1044 return msi_export_record( arg, row, 1 );
1045 }
1046
1047 static UINT msi_export_forcecodepage( HANDLE handle, UINT codepage )
1048 {
1049 static const char fmt[] = "\r\n\r\n%u\t_ForceCodepage\r\n";
1050 char data[sizeof(fmt) + 10];
1051 DWORD sz;
1052
1053 sprintf( data, fmt, codepage );
1054
1055 sz = lstrlenA(data) + 1;
1056 if (!WriteFile(handle, data, sz, &sz, NULL))
1057 return ERROR_FUNCTION_FAILED;
1058
1059 return ERROR_SUCCESS;
1060 }
1061
1062 static UINT msi_export_summaryinformation( MSIDATABASE *db, HANDLE handle )
1063 {
1064 static const char header[] = "PropertyId\tValue\r\n"
1065 "i2\tl255\r\n"
1066 "_SummaryInformation\tPropertyId\r\n";
1067 DWORD sz;
1068
1069 sz = lstrlenA(header);
1070 if (!WriteFile(handle, header, sz, &sz, NULL))
1071 return ERROR_WRITE_FAULT;
1072
1073 return msi_export_suminfo( db, handle );
1074 }
1075
1076 static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
1077 LPCWSTR folder, LPCWSTR file )
1078 {
1079 static const WCHAR summaryinformation[] = {
1080 '_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
1081 static const WCHAR query[] = {
1082 's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
1083 static const WCHAR forcecodepage[] = {
1084 '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 };
1085 MSIRECORD *rec = NULL;
1086 MSIQUERY *view = NULL;
1087 LPWSTR filename;
1088 HANDLE handle;
1089 UINT len, r;
1090
1091 TRACE("%p %s %s %s\n", db, debugstr_w(table),
1092 debugstr_w(folder), debugstr_w(file) );
1093
1094 if( folder == NULL || file == NULL )
1095 return ERROR_INVALID_PARAMETER;
1096
1097 len = lstrlenW(folder) + lstrlenW(file) + 2;
1098 filename = msi_alloc(len * sizeof (WCHAR));
1099 if (!filename)
1100 return ERROR_OUTOFMEMORY;
1101
1102 lstrcpyW( filename, folder );
1103 lstrcatW( filename, szBackSlash );
1104 lstrcatW( filename, file );
1105
1106 handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
1107 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
1108 msi_free( filename );
1109 if (handle == INVALID_HANDLE_VALUE)
1110 return ERROR_FUNCTION_FAILED;
1111
1112 if (!strcmpW( table, forcecodepage ))
1113 {
1114 UINT codepage = msi_get_string_table_codepage( db->strings );
1115 r = msi_export_forcecodepage( handle, codepage );
1116 goto done;
1117 }
1118
1119 if (!strcmpW( table, summaryinformation ))
1120 {
1121 r = msi_export_summaryinformation( db, handle );
1122 goto done;
1123 }
1124
1125 r = MSI_OpenQuery( db, &view, query, table );
1126 if (r == ERROR_SUCCESS)
1127 {
1128 struct row_export_info row_export_info = { handle, folder, table };
1129
1130 /* write out row 1, the column names */
1131 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
1132 if (r == ERROR_SUCCESS)
1133 {
1134 msi_export_record( &row_export_info, rec, 1 );
1135 msiobj_release( &rec->hdr );
1136 }
1137
1138 /* write out row 2, the column types */
1139 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
1140 if (r == ERROR_SUCCESS)
1141 {
1142 msi_export_record( &row_export_info, rec, 1 );
1143 msiobj_release( &rec->hdr );
1144 }
1145
1146 /* write out row 3, the table name + keys */
1147 r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
1148 if (r == ERROR_SUCCESS)
1149 {
1150 MSI_RecordSetStringW( rec, 0, table );
1151 msi_export_record( &row_export_info, rec, 0 );
1152 msiobj_release( &rec->hdr );
1153 }
1154
1155 /* write out row 4 onwards, the data */
1156 r = MSI_IterateRecords( view, 0, msi_export_row, &row_export_info );
1157 msiobj_release( &view->hdr );
1158 }
1159
1160 done:
1161 CloseHandle( handle );
1162 return r;
1163 }
1164
1165 /***********************************************************************
1166 * MsiExportDatabaseW [MSI.@]
1167 *
1168 * Writes a file containing the table data as tab separated ASCII.
1169 *
1170 * The format is as follows:
1171 *
1172 * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
1173 * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
1174 * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
1175 *
1176 * Followed by the data, starting at row 1 with one row per line
1177 *
1178 * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
1179 */
1180 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
1181 LPCWSTR szFolder, LPCWSTR szFilename )
1182 {
1183 MSIDATABASE *db;
1184 UINT r;
1185
1186 TRACE("%x %s %s %s\n", handle, debugstr_w(szTable),
1187 debugstr_w(szFolder), debugstr_w(szFilename));
1188
1189 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
1190 if( !db )
1191 {
1192 IWineMsiRemoteDatabase *remote_database;
1193
1194 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
1195 if ( !remote_database )
1196 return ERROR_INVALID_HANDLE;
1197
1198 IWineMsiRemoteDatabase_Release( remote_database );
1199 WARN("MsiDatabaseExport not allowed during a custom action!\n");
1200
1201 return ERROR_SUCCESS;
1202 }
1203
1204 r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
1205 msiobj_release( &db->hdr );
1206 return r;
1207 }
1208
1209 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
1210 LPCSTR szFolder, LPCSTR szFilename )
1211 {
1212 LPWSTR path = NULL, file = NULL, table = NULL;
1213 UINT r = ERROR_OUTOFMEMORY;
1214
1215 TRACE("%x %s %s %s\n", handle, debugstr_a(szTable),
1216 debugstr_a(szFolder), debugstr_a(szFilename));
1217
1218 if( szTable )
1219 {
1220 table = strdupAtoW( szTable );
1221 if( !table )
1222 goto end;
1223 }
1224
1225 if( szFolder )
1226 {
1227 path = strdupAtoW( szFolder );
1228 if( !path )
1229 goto end;
1230 }
1231
1232 if( szFilename )
1233 {
1234 file = strdupAtoW( szFilename );
1235 if( !file )
1236 goto end;
1237 }
1238
1239 r = MsiDatabaseExportW( handle, table, path, file );
1240
1241 end:
1242 msi_free( table );
1243 msi_free( path );
1244 msi_free( file );
1245
1246 return r;
1247 }
1248
1249 UINT WINAPI MsiDatabaseMergeA(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1250 LPCSTR szTableName)
1251 {
1252 UINT r;
1253 LPWSTR table;
1254
1255 TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1256 debugstr_a(szTableName));
1257
1258 table = strdupAtoW(szTableName);
1259 r = MsiDatabaseMergeW(hDatabase, hDatabaseMerge, table);
1260
1261 msi_free(table);
1262 return r;
1263 }
1264
1265 typedef struct _tagMERGETABLE
1266 {
1267 struct list entry;
1268 struct list rows;
1269 LPWSTR name;
1270 DWORD numconflicts;
1271 LPWSTR *columns;
1272 DWORD numcolumns;
1273 LPWSTR *types;
1274 DWORD numtypes;
1275 LPWSTR *labels;
1276 DWORD numlabels;
1277 } MERGETABLE;
1278
1279 typedef struct _tagMERGEROW
1280 {
1281 struct list entry;
1282 MSIRECORD *data;
1283 } MERGEROW;
1284
1285 typedef struct _tagMERGEDATA
1286 {
1287 MSIDATABASE *db;
1288 MSIDATABASE *merge;
1289 MERGETABLE *curtable;
1290 MSIQUERY *curview;
1291 struct list *tabledata;
1292 } MERGEDATA;
1293
1294 static BOOL merge_type_match(LPCWSTR type1, LPCWSTR type2)
1295 {
1296 if (((type1[0] == 'l') || (type1[0] == 's')) &&
1297 ((type2[0] == 'l') || (type2[0] == 's')))
1298 return TRUE;
1299
1300 if (((type1[0] == 'L') || (type1[0] == 'S')) &&
1301 ((type2[0] == 'L') || (type2[0] == 'S')))
1302 return TRUE;
1303
1304 return !strcmpW( type1, type2 );
1305 }
1306
1307 static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview)
1308 {
1309 MSIRECORD *dbrec, *mergerec;
1310 UINT r, i, count;
1311
1312 r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec);
1313 if (r != ERROR_SUCCESS)
1314 return r;
1315
1316 r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec);
1317 if (r != ERROR_SUCCESS)
1318 {
1319 msiobj_release(&dbrec->hdr);
1320 return r;
1321 }
1322
1323 count = MSI_RecordGetFieldCount(dbrec);
1324 for (i = 1; i <= count; i++)
1325 {
1326 if (!MSI_RecordGetString(mergerec, i))
1327 break;
1328
1329 if (strcmpW( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) ))
1330 {
1331 r = ERROR_DATATYPE_MISMATCH;
1332 goto done;
1333 }
1334 }
1335
1336 msiobj_release(&dbrec->hdr);
1337 msiobj_release(&mergerec->hdr);
1338 dbrec = mergerec = NULL;
1339
1340 r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec);
1341 if (r != ERROR_SUCCESS)
1342 return r;
1343
1344 r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec);
1345 if (r != ERROR_SUCCESS)
1346 {
1347 msiobj_release(&dbrec->hdr);
1348 return r;
1349 }
1350
1351 count = MSI_RecordGetFieldCount(dbrec);
1352 for (i = 1; i <= count; i++)
1353 {
1354 if (!MSI_RecordGetString(mergerec, i))
1355 break;
1356
1357 if (!merge_type_match(MSI_RecordGetString(dbrec, i),
1358 MSI_RecordGetString(mergerec, i)))
1359 {
1360 r = ERROR_DATATYPE_MISMATCH;
1361 break;
1362 }
1363 }
1364
1365 done:
1366 msiobj_release(&dbrec->hdr);
1367 msiobj_release(&mergerec->hdr);
1368
1369 return r;
1370 }
1371
1372 static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb,
1373 LPCWSTR table)
1374 {
1375 MSIRECORD *dbrec, *mergerec = NULL;
1376 UINT r, i, count;
1377
1378 r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec);
1379 if (r != ERROR_SUCCESS)
1380 return r;
1381
1382 r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec);
1383 if (r != ERROR_SUCCESS)
1384 goto done;
1385
1386 count = MSI_RecordGetFieldCount(dbrec);
1387 if (count != MSI_RecordGetFieldCount(mergerec))
1388 {
1389 r = ERROR_DATATYPE_MISMATCH;
1390 goto done;
1391 }
1392
1393 for (i = 1; i <= count; i++)
1394 {
1395 if (strcmpW( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) ))
1396 {
1397 r = ERROR_DATATYPE_MISMATCH;
1398 goto done;
1399 }
1400 }
1401
1402 done:
1403 msiobj_release(&dbrec->hdr);
1404 msiobj_release(&mergerec->hdr);
1405
1406 return r;
1407 }
1408
1409 static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec)
1410 {
1411 MSIRECORD *colnames;
1412 LPWSTR str, val;
1413 UINT r, i = 0, sz = 0;
1414 int cmp;
1415
1416 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames);
1417 if (r != ERROR_SUCCESS)
1418 return NULL;
1419
1420 do
1421 {
1422 str = msi_dup_record_field(colnames, ++i);
1423 cmp = strcmpW( key, str );
1424 msi_free(str);
1425 } while (cmp);
1426
1427 msiobj_release(&colnames->hdr);
1428
1429 r = MSI_RecordGetStringW(rec, i, NULL, &sz);
1430 if (r != ERROR_SUCCESS)
1431 return NULL;
1432 sz++;
1433
1434 if (MSI_RecordGetString(rec, i)) /* check record field is a string */
1435 {
1436 /* quote string record fields */
1437 const WCHAR szQuote[] = {'\'', 0};
1438 sz += 2;
1439 val = msi_alloc(sz*sizeof(WCHAR));
1440 if (!val)
1441 return NULL;
1442
1443 lstrcpyW(val, szQuote);
1444 r = MSI_RecordGetStringW(rec, i, val+1, &sz);
1445 lstrcpyW(val+1+sz, szQuote);
1446 }
1447 else
1448 {
1449 /* do not quote integer record fields */
1450 val = msi_alloc(sz*sizeof(WCHAR));
1451 if (!val)
1452 return NULL;
1453
1454 r = MSI_RecordGetStringW(rec, i, val, &sz);
1455 }
1456
1457 if (r != ERROR_SUCCESS)
1458 {
1459 ERR("failed to get string!\n");
1460 msi_free(val);
1461 return NULL;
1462 }
1463
1464 return val;
1465 }
1466
1467 static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view,
1468 LPWSTR table, MSIRECORD *rec)
1469 {
1470 LPWSTR query = NULL, clause = NULL, val;
1471 LPCWSTR setptr, key;
1472 DWORD size, oldsize;
1473 MSIRECORD *keys;
1474 UINT r, i, count;
1475
1476 static const WCHAR keyset[] = {
1477 '`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0};
1478 static const WCHAR lastkeyset[] = {
1479 '`','%','s','`',' ','=',' ','%','s',' ',0};
1480 static const WCHAR fmt[] = {'S','E','L','E','C','T',' ','*',' ',
1481 'F','R','O','M',' ','`','%','s','`',' ',
1482 'W','H','E','R','E',' ','%','s',0};
1483
1484 r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys);
1485 if (r != ERROR_SUCCESS)
1486 return NULL;
1487
1488 clause = msi_alloc_zero(sizeof(WCHAR));
1489 if (!clause)
1490 goto done;
1491
1492 size = 1;
1493 count = MSI_RecordGetFieldCount(keys);
1494 for (i = 1; i <= count; i++)
1495 {
1496 key = MSI_RecordGetString(keys, i);
1497 val = get_key_value(view, key, rec);
1498
1499 if (i == count)
1500 setptr = lastkeyset;
1501 else
1502 setptr = keyset;
1503
1504 oldsize = size;
1505 size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4;
1506 clause = msi_realloc(clause, size * sizeof (WCHAR));
1507 if (!clause)
1508 {
1509 msi_free(val);
1510 goto done;
1511 }
1512
1513 sprintfW(clause + oldsize - 1, setptr, key, val);
1514 msi_free(val);
1515 }
1516
1517 size = lstrlenW(fmt) + lstrlenW(table) + lstrlenW(clause) + 1;
1518 query = msi_alloc(size * sizeof(WCHAR));
1519 if (!query)
1520 goto done;
1521
1522 sprintfW(query, fmt, table, clause);
1523
1524 done:
1525 msi_free(clause);
1526 msiobj_release(&keys->hdr);
1527 return query;
1528 }
1529
1530 static UINT merge_diff_row(MSIRECORD *rec, LPVOID param)
1531 {
1532 MERGEDATA *data = param;
1533 MERGETABLE *table = data->curtable;
1534 MERGEROW *mergerow;
1535 MSIQUERY *dbview = NULL;
1536 MSIRECORD *row = NULL;
1537 LPWSTR query = NULL;
1538 UINT r = ERROR_SUCCESS;
1539
1540 if (TABLE_Exists(data->db, table->name))
1541 {
1542 query = create_diff_row_query(data->merge, data->curview, table->name, rec);
1543 if (!query)
1544 return ERROR_OUTOFMEMORY;
1545
1546 r = MSI_DatabaseOpenViewW(data->db, query, &dbview);
1547 if (r != ERROR_SUCCESS)
1548 goto done;
1549
1550 r = MSI_ViewExecute(dbview, NULL);
1551 if (r != ERROR_SUCCESS)
1552 goto done;
1553
1554 r = MSI_ViewFetch(dbview, &row);
1555 if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row))
1556 {
1557 table->numconflicts++;
1558 goto done;
1559 }
1560 else if (r != ERROR_NO_MORE_ITEMS)
1561 goto done;
1562
1563 r = ERROR_SUCCESS;
1564 }
1565
1566 mergerow = msi_alloc(sizeof(MERGEROW));
1567 if (!mergerow)
1568 {
1569 r = ERROR_OUTOFMEMORY;
1570 goto done;
1571 }
1572
1573 mergerow->data = MSI_CloneRecord(rec);
1574 if (!mergerow->data)
1575 {
1576 r = ERROR_OUTOFMEMORY;
1577 msi_free(mergerow);
1578 goto done;
1579 }
1580
1581 list_add_tail(&table->rows, &mergerow->entry);
1582
1583 done:
1584 msi_free(query);
1585 msiobj_release(&row->hdr);
1586 msiobj_release(&dbview->hdr);
1587 return r;
1588 }
1589
1590 static UINT msi_get_table_labels(MSIDATABASE *db, LPCWSTR table, LPWSTR **labels, DWORD *numlabels)
1591 {
1592 UINT r, i, count;
1593 MSIRECORD *prec = NULL;
1594
1595 r = MSI_DatabaseGetPrimaryKeys(db, table, &prec);
1596 if (r != ERROR_SUCCESS)
1597 return r;
1598
1599 count = MSI_RecordGetFieldCount(prec);
1600 *numlabels = count + 1;
1601 *labels = msi_alloc((*numlabels)*sizeof(LPWSTR));
1602 if (!*labels)
1603 {
1604 r = ERROR_OUTOFMEMORY;
1605 goto end;
1606 }
1607
1608 (*labels)[0] = strdupW(table);
1609 for (i=1; i<=count; i++ )
1610 {
1611 (*labels)[i] = strdupW(MSI_RecordGetString(prec, i));
1612 }
1613
1614 end:
1615 msiobj_release( &prec->hdr );
1616 return r;
1617 }
1618
1619 static UINT msi_get_query_columns(MSIQUERY *query, LPWSTR **columns, DWORD *numcolumns)
1620 {
1621 UINT r, i, count;
1622 MSIRECORD *prec = NULL;
1623
1624 r = MSI_ViewGetColumnInfo(query, MSICOLINFO_NAMES, &prec);
1625 if (r != ERROR_SUCCESS)
1626 return r;
1627
1628 count = MSI_RecordGetFieldCount(prec);
1629 *columns = msi_alloc(count*sizeof(LPWSTR));
1630 if (!*columns)
1631 {
1632 r = ERROR_OUTOFMEMORY;
1633 goto end;
1634 }
1635
1636 for (i=1; i<=count; i++ )
1637 {
1638 (*columns)[i-1] = strdupW(MSI_RecordGetString(prec, i));
1639 }
1640
1641 *numcolumns = count;
1642
1643 end:
1644 msiobj_release( &prec->hdr );
1645 return r;
1646 }
1647
1648 static UINT msi_get_query_types(MSIQUERY *query, LPWSTR **types, DWORD *numtypes)
1649 {
1650 UINT r, i, count;
1651 MSIRECORD *prec = NULL;
1652
1653 r = MSI_ViewGetColumnInfo(query, MSICOLINFO_TYPES, &prec);
1654 if (r != ERROR_SUCCESS)
1655 return r;
1656
1657 count = MSI_RecordGetFieldCount(prec);
1658 *types = msi_alloc(count*sizeof(LPWSTR));
1659 if (!*types)
1660 {
1661 r = ERROR_OUTOFMEMORY;
1662 goto end;
1663 }
1664
1665 *numtypes = count;
1666 for (i=1; i<=count; i++ )
1667 {
1668 (*types)[i-1] = strdupW(MSI_RecordGetString(prec, i));
1669 }
1670
1671 end:
1672 msiobj_release( &prec->hdr );
1673 return r;
1674 }
1675
1676 static void merge_free_rows(MERGETABLE *table)
1677 {
1678 struct list *item, *cursor;
1679
1680 LIST_FOR_EACH_SAFE(item, cursor, &table->rows)
1681 {
1682 MERGEROW *row = LIST_ENTRY(item, MERGEROW, entry);
1683
1684 list_remove(&row->entry);
1685 msiobj_release(&row->data->hdr);
1686 msi_free(row);
1687 }
1688 }
1689
1690 static void free_merge_table(MERGETABLE *table)
1691 {
1692 UINT i;
1693
1694 if (table->labels != NULL)
1695 {
1696 for (i = 0; i < table->numlabels; i++)
1697 msi_free(table->labels[i]);
1698
1699 msi_free(table->labels);
1700 }
1701
1702 if (table->columns != NULL)
1703 {
1704 for (i = 0; i < table->numcolumns; i++)
1705 msi_free(table->columns[i]);
1706
1707 msi_free(table->columns);
1708 }
1709
1710 if (table->types != NULL)
1711 {
1712 for (i = 0; i < table->numtypes; i++)
1713 msi_free(table->types[i]);
1714
1715 msi_free(table->types);
1716 }
1717
1718 msi_free(table->name);
1719 merge_free_rows(table);
1720
1721 msi_free(table);
1722 }
1723
1724 static UINT msi_get_merge_table (MSIDATABASE *db, LPCWSTR name, MERGETABLE **ptable)
1725 {
1726 UINT r;
1727 MERGETABLE *table;
1728 MSIQUERY *mergeview = NULL;
1729
1730 static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1731 'F','R','O','M',' ','`','%','s','`',0};
1732
1733 table = msi_alloc_zero(sizeof(MERGETABLE));
1734 if (!table)
1735 {
1736 *ptable = NULL;
1737 return ERROR_OUTOFMEMORY;
1738 }
1739
1740 r = msi_get_table_labels(db, name, &table->labels, &table->numlabels);
1741 if (r != ERROR_SUCCESS)
1742 goto err;
1743
1744 r = MSI_OpenQuery(db, &mergeview, query, name);
1745 if (r != ERROR_SUCCESS)
1746 goto err;
1747
1748 r = msi_get_query_columns(mergeview, &table->columns, &table->numcolumns);
1749 if (r != ERROR_SUCCESS)
1750 goto err;
1751
1752 r = msi_get_query_types(mergeview, &table->types, &table->numtypes);
1753 if (r != ERROR_SUCCESS)
1754 goto err;
1755
1756 list_init(&table->rows);
1757
1758 table->name = strdupW(name);
1759 table->numconflicts = 0;
1760
1761 msiobj_release(&mergeview->hdr);
1762 *ptable = table;
1763 return ERROR_SUCCESS;
1764
1765 err:
1766 msiobj_release(&mergeview->hdr);
1767 free_merge_table(table);
1768 *ptable = NULL;
1769 return r;
1770 }
1771
1772 static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
1773 {
1774 MERGEDATA *data = param;
1775 MERGETABLE *table;
1776 MSIQUERY *dbview = NULL;
1777 MSIQUERY *mergeview = NULL;
1778 LPCWSTR name;
1779 UINT r;
1780
1781 static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1782 'F','R','O','M',' ','`','%','s','`',0};
1783
1784 name = MSI_RecordGetString(rec, 1);
1785
1786 r = MSI_OpenQuery(data->merge, &mergeview, query, name);
1787 if (r != ERROR_SUCCESS)
1788 goto done;
1789
1790 if (TABLE_Exists(data->db, name))
1791 {
1792 r = MSI_OpenQuery(data->db, &dbview, query, name);
1793 if (r != ERROR_SUCCESS)
1794 goto done;
1795
1796 r = merge_verify_colnames(dbview, mergeview);
1797 if (r != ERROR_SUCCESS)
1798 goto done;
1799
1800 r = merge_verify_primary_keys(data->db, data->merge, name);
1801 if (r != ERROR_SUCCESS)
1802 goto done;
1803 }
1804
1805 r = msi_get_merge_table(data->merge, name, &table);
1806 if (r != ERROR_SUCCESS)
1807 goto done;
1808
1809 data->curtable = table;
1810 data->curview = mergeview;
1811 r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
1812 if (r != ERROR_SUCCESS)
1813 {
1814 free_merge_table(table);
1815 goto done;
1816 }
1817
1818 list_add_tail(data->tabledata, &table->entry);
1819
1820 done:
1821 msiobj_release(&dbview->hdr);
1822 msiobj_release(&mergeview->hdr);
1823 return r;
1824 }
1825
1826 static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge,
1827 struct list *tabledata)
1828 {
1829 static const WCHAR query[] = {
1830 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1831 '`','_','T','a','b','l','e','s','`',0};
1832 MSIQUERY *view;
1833 MERGEDATA data;
1834 UINT r;
1835
1836 r = MSI_DatabaseOpenViewW(merge, query, &view);
1837 if (r != ERROR_SUCCESS)
1838 return r;
1839
1840 data.db = db;
1841 data.merge = merge;
1842 data.tabledata = tabledata;
1843 r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data);
1844 msiobj_release(&view->hdr);
1845 return r;
1846 }
1847
1848 static UINT merge_table(MSIDATABASE *db, MERGETABLE *table)
1849 {
1850 UINT r;
1851 MERGEROW *row;
1852 MSIVIEW *tv;
1853
1854 if (!TABLE_Exists(db, table->name))
1855 {
1856 r = msi_add_table_to_db(db, table->columns, table->types,
1857 table->labels, table->numlabels, table->numcolumns);
1858 if (r != ERROR_SUCCESS)
1859 return ERROR_FUNCTION_FAILED;
1860 }
1861
1862 LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry)
1863 {
1864 r = TABLE_CreateView(db, table->name, &tv);
1865 if (r != ERROR_SUCCESS)
1866 return r;
1867
1868 r = tv->ops->insert_row(tv, row->data, -1, FALSE);
1869 tv->ops->delete(tv);
1870
1871 if (r != ERROR_SUCCESS)
1872 return r;
1873 }
1874
1875 return ERROR_SUCCESS;
1876 }
1877
1878 static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error,
1879 LPWSTR table, DWORD numconflicts)
1880 {
1881 UINT r;
1882 MSIQUERY *view;
1883
1884 static const WCHAR create[] = {
1885 'C','R','E','A','T','E',' ','T','A','B','L','E',' ',
1886 '`','%','s','`',' ','(','`','T','a','b','l','e','`',' ',
1887 'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ',
1888 'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e',
1889 'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ',
1890 'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ',
1891 'K','E','Y',' ','`','T','a','b','l','e','`',')',0};
1892 static const WCHAR insert[] = {
1893 'I','N','S','E','R','T',' ','I','N','T','O',' ',
1894 '`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ',
1895 '`','N','u','m','R','o','w','M','e','r','g','e',
1896 'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S',
1897 ' ','(','\'','%','s','\'',',',' ','%','d',')',0};
1898
1899 if (!TABLE_Exists(db, error))
1900 {
1901 r = MSI_OpenQuery(db, &view, create, error);
1902 if (r != ERROR_SUCCESS)
1903 return r;
1904
1905 r = MSI_ViewExecute(view, NULL);
1906 msiobj_release(&view->hdr);
1907 if (r != ERROR_SUCCESS)
1908 return r;
1909 }
1910
1911 r = MSI_OpenQuery(db, &view, insert, error, table, numconflicts);
1912 if (r != ERROR_SUCCESS)
1913 return r;
1914
1915 r = MSI_ViewExecute(view, NULL);
1916 msiobj_release(&view->hdr);
1917 return r;
1918 }
1919
1920 UINT WINAPI MsiDatabaseMergeW(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1921 LPCWSTR szTableName)
1922 {
1923 struct list tabledata = LIST_INIT(tabledata);
1924 struct list *item, *cursor;
1925 MSIDATABASE *db, *merge;
1926 MERGETABLE *table;
1927 BOOL conflicts;
1928 UINT r;
1929
1930 TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1931 debugstr_w(szTableName));
1932
1933 if (szTableName && !*szTableName)
1934 return ERROR_INVALID_TABLE;
1935
1936 db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE);
1937 merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE);
1938 if (!db || !merge)
1939 {
1940 r = ERROR_INVALID_HANDLE;
1941 goto done;
1942 }
1943
1944 r = gather_merge_data(db, merge, &tabledata);
1945 if (r != ERROR_SUCCESS)
1946 goto done;
1947
1948 conflicts = FALSE;
1949 LIST_FOR_EACH_ENTRY(table, &tabledata, MERGETABLE, entry)
1950 {
1951 if (table->numconflicts)
1952 {
1953 conflicts = TRUE;
1954
1955 r = update_merge_errors(db, szTableName, table->name,
1956 table->numconflicts);
1957 if (r != ERROR_SUCCESS)
1958 break;
1959 }
1960 else
1961 {
1962 r = merge_table(db, table);
1963 if (r != ERROR_SUCCESS)
1964 break;
1965 }
1966 }
1967
1968 LIST_FOR_EACH_SAFE(item, cursor, &tabledata)
1969 {
1970 MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry);
1971 list_remove(&table->entry);
1972 free_merge_table(table);
1973 }
1974
1975 if (conflicts)
1976 r = ERROR_FUNCTION_FAILED;
1977
1978 done:
1979 msiobj_release(&db->hdr);
1980 msiobj_release(&merge->hdr);
1981 return r;
1982 }
1983
1984 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
1985 {
1986 MSIDBSTATE ret = MSIDBSTATE_READ;
1987 MSIDATABASE *db;
1988
1989 TRACE("%d\n", handle);
1990
1991 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
1992 if( !db )
1993 {
1994 WARN("MsiGetDatabaseState not allowed during a custom action!\n");
1995 return MSIDBSTATE_ERROR;
1996 }
1997
1998 if (db->mode != MSIDBOPEN_READONLY )
1999 ret = MSIDBSTATE_WRITE;
2000 msiobj_release( &db->hdr );
2001
2002 return ret;
2003 }
2004
2005 typedef struct _msi_remote_database_impl {
2006 IWineMsiRemoteDatabase IWineMsiRemoteDatabase_iface;
2007 MSIHANDLE database;
2008 LONG refs;
2009 } msi_remote_database_impl;
2010
2011 static inline msi_remote_database_impl *impl_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase *iface )
2012 {
2013 return CONTAINING_RECORD(iface, msi_remote_database_impl, IWineMsiRemoteDatabase_iface);
2014 }
2015
2016 static HRESULT WINAPI mrd_QueryInterface( IWineMsiRemoteDatabase *iface,
2017 REFIID riid,LPVOID *ppobj)
2018 {
2019 if( IsEqualCLSID( riid, &IID_IUnknown ) ||
2020 IsEqualCLSID( riid, &IID_IWineMsiRemoteDatabase ) )
2021 {
2022 IWineMsiRemoteDatabase_AddRef( iface );
2023 *ppobj = iface;
2024 return S_OK;
2025 }
2026
2027 return E_NOINTERFACE;
2028 }
2029
2030 static ULONG WINAPI mrd_AddRef( IWineMsiRemoteDatabase *iface )
2031 {
2032 msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface );
2033
2034 return InterlockedIncrement( &This->refs );
2035 }
2036
2037 static ULONG WINAPI mrd_Release( IWineMsiRemoteDatabase *iface )
2038 {
2039 msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface );
2040 ULONG r;
2041
2042 r = InterlockedDecrement( &This->refs );
2043 if (r == 0)
2044 {
2045 MsiCloseHandle( This->database );
2046 msi_free( This );
2047 }
2048 return r;
2049 }
2050
2051 static HRESULT WINAPI mrd_IsTablePersistent( IWineMsiRemoteDatabase *iface,
2052 LPCWSTR table, MSICONDITION *persistent )
2053 {
2054 msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface );
2055 *persistent = MsiDatabaseIsTablePersistentW(This->database, table);
2056 return S_OK;
2057 }
2058
2059 static HRESULT WINAPI mrd_GetPrimaryKeys( IWineMsiRemoteDatabase *iface,
2060 LPCWSTR table, MSIHANDLE *keys )
2061 {
2062 msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface );
2063 UINT r = MsiDatabaseGetPrimaryKeysW(This->database, table, keys);
2064 return HRESULT_FROM_WIN32(r);
2065 }
2066
2067 static HRESULT WINAPI mrd_GetSummaryInformation( IWineMsiRemoteDatabase *iface,
2068 UINT updatecount, MSIHANDLE *suminfo )
2069 {
2070 msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface );
2071 UINT r = MsiGetSummaryInformationW(This->database, NULL, updatecount, suminfo);
2072 return HRESULT_FROM_WIN32(r);
2073 }
2074
2075 static HRESULT WINAPI mrd_OpenView( IWineMsiRemoteDatabase *iface,
2076 LPCWSTR query, MSIHANDLE *view )
2077 {
2078 msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface );
2079 UINT r = MsiDatabaseOpenViewW(This->database, query, view);
2080 return HRESULT_FROM_WIN32(r);
2081 }
2082
2083 static HRESULT WINAPI mrd_SetMsiHandle( IWineMsiRemoteDatabase *iface, MSIHANDLE handle )
2084 {
2085 msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface );
2086 This->database = handle;
2087 return S_OK;
2088 }
2089
2090 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl =
2091 {
2092 mrd_QueryInterface,
2093 mrd_AddRef,
2094 mrd_Release,
2095 mrd_IsTablePersistent,
2096 mrd_GetPrimaryKeys,
2097 mrd_GetSummaryInformation,
2098 mrd_OpenView,
2099 mrd_SetMsiHandle,
2100 };
2101
2102 HRESULT create_msi_remote_database( IUnknown *pOuter, LPVOID *ppObj )
2103 {
2104 msi_remote_database_impl *This;
2105
2106 This = msi_alloc( sizeof *This );
2107 if (!This)
2108 return E_OUTOFMEMORY;
2109
2110 This->IWineMsiRemoteDatabase_iface.lpVtbl = &msi_remote_database_vtbl;
2111 This->database = 0;
2112 This->refs = 1;
2113
2114 *ppObj = &This->IWineMsiRemoteDatabase_iface;
2115
2116 return S_OK;
2117 }