xref: /reactos/dll/win32/msi/database.c (revision f4be6dc3)
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