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