ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

database.c
Go to the documentation of this file.
00001 /*
00002  * Implementation of the Microsoft Installer (msi.dll)
00003  *
00004  * Copyright 2002,2003,2004,2005 Mike McCormack for CodeWeavers
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00019  */
00020 
00021 #include <stdarg.h>
00022 #include <stdio.h>
00023 
00024 #define COBJMACROS
00025 #define NONAMELESSUNION
00026 
00027 #include "windef.h"
00028 #include "winbase.h"
00029 #include "winreg.h"
00030 #include "winnls.h"
00031 #include "wine/debug.h"
00032 #include "wine/unicode.h"
00033 #include "msi.h"
00034 #include "msiquery.h"
00035 #include "msipriv.h"
00036 #include "objidl.h"
00037 #include "objbase.h"
00038 #include "msiserver.h"
00039 #include "query.h"
00040 
00041 #include "initguid.h"
00042 
00043 WINE_DEFAULT_DEBUG_CHANNEL(msi);
00044 
00045 /*
00046  *  .MSI  file format
00047  *
00048  *  An .msi file is a structured storage file.
00049  *  It contains a number of streams.
00050  *  A stream for each table in the database.
00051  *  Two streams for the string table in the database.
00052  *  Any binary data in a table is a reference to a stream.
00053  */
00054 
00055 #define IS_INTMSIDBOPEN(x)      (((ULONG_PTR)(x) >> 16) == 0)
00056 
00057 typedef struct tagMSITRANSFORM {
00058     struct list entry;
00059     IStorage *stg;
00060 } MSITRANSFORM;
00061 
00062 typedef struct tagMSISTREAM {
00063     struct list entry;
00064     IStorage *stg;
00065     IStream *stm;
00066 } MSISTREAM;
00067 
00068 static UINT find_open_stream( MSIDATABASE *db, IStorage *stg, LPCWSTR name, IStream **stm )
00069 {
00070     MSISTREAM *stream;
00071 
00072     LIST_FOR_EACH_ENTRY( stream, &db->streams, MSISTREAM, entry )
00073     {
00074         HRESULT r;
00075         STATSTG stat;
00076 
00077         if (stream->stg != stg) continue;
00078 
00079         r = IStream_Stat( stream->stm, &stat, 0 );
00080         if( FAILED( r ) )
00081         {
00082             WARN("failed to stat stream r = %08x!\n", r);
00083             continue;
00084         }
00085 
00086         if( !strcmpW( name, stat.pwcsName ) )
00087         {
00088             TRACE("found %s\n", debugstr_w(name));
00089             *stm = stream->stm;
00090             CoTaskMemFree( stat.pwcsName );
00091             return ERROR_SUCCESS;
00092         }
00093 
00094         CoTaskMemFree( stat.pwcsName );
00095     }
00096 
00097     return ERROR_FUNCTION_FAILED;
00098 }
00099 
00100 UINT msi_clone_open_stream( MSIDATABASE *db, IStorage *stg, LPCWSTR name, IStream **stm )
00101 {
00102     IStream *stream;
00103 
00104     if (find_open_stream( db, stg, name, &stream ) == ERROR_SUCCESS)
00105     {
00106         HRESULT r;
00107         LARGE_INTEGER pos;
00108 
00109         r = IStream_Clone( stream, stm );
00110         if( FAILED( r ) )
00111         {
00112             WARN("failed to clone stream r = %08x!\n", r);
00113             return ERROR_FUNCTION_FAILED;
00114         }
00115 
00116         pos.QuadPart = 0;
00117         r = IStream_Seek( *stm, pos, STREAM_SEEK_SET, NULL );
00118         if( FAILED( r ) )
00119         {
00120             IStream_Release( *stm );
00121             return ERROR_FUNCTION_FAILED;
00122         }
00123 
00124         return ERROR_SUCCESS;
00125     }
00126 
00127     return ERROR_FUNCTION_FAILED;
00128 }
00129 
00130 UINT msi_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
00131 {
00132     HRESULT r;
00133     IStorage *stg;
00134     WCHAR decoded[MAX_STREAM_NAME_LEN];
00135 
00136     decode_streamname( stname, decoded );
00137     TRACE("%s -> %s\n", debugstr_w(stname), debugstr_w(decoded));
00138 
00139     if (msi_clone_open_stream( db, db->storage, stname, stm ) == ERROR_SUCCESS)
00140         return ERROR_SUCCESS;
00141 
00142     r = IStorage_OpenStream( db->storage, stname, NULL,
00143                              STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
00144     if( FAILED( r ) )
00145     {
00146         MSITRANSFORM *transform;
00147 
00148         LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
00149         {
00150             r = IStorage_OpenStream( transform->stg, stname, NULL,
00151                                      STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
00152             if (SUCCEEDED(r))
00153             {
00154                 stg = transform->stg;
00155                 break;
00156             }
00157         }
00158     }
00159     else stg = db->storage;
00160 
00161     if( SUCCEEDED(r) )
00162     {
00163         MSISTREAM *stream;
00164 
00165         if (!(stream = msi_alloc( sizeof(MSISTREAM) ))) return ERROR_NOT_ENOUGH_MEMORY;
00166         stream->stg = stg;
00167         IStream_AddRef( stg );
00168         stream->stm = *stm;
00169         IStream_AddRef( *stm );
00170         list_add_tail( &db->streams, &stream->entry );
00171     }
00172 
00173     return SUCCEEDED(r) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
00174 }
00175 
00176 static void free_transforms( MSIDATABASE *db )
00177 {
00178     while( !list_empty( &db->transforms ) )
00179     {
00180         MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ),
00181                                       MSITRANSFORM, entry );
00182         list_remove( &t->entry );
00183         IStorage_Release( t->stg );
00184         msi_free( t );
00185     }
00186 }
00187 
00188 void msi_destroy_stream( MSIDATABASE *db, const WCHAR *stname )
00189 {
00190     MSISTREAM *stream, *stream2;
00191 
00192     LIST_FOR_EACH_ENTRY_SAFE( stream, stream2, &db->streams, MSISTREAM, entry )
00193     {
00194         HRESULT r;
00195         STATSTG stat;
00196 
00197         r = IStream_Stat( stream->stm, &stat, 0 );
00198         if (FAILED(r))
00199         {
00200             WARN("failed to stat stream r = %08x\n", r);
00201             continue;
00202         }
00203 
00204         if (!strcmpW( stname, stat.pwcsName ))
00205         {
00206             TRACE("destroying %s\n", debugstr_w(stname));
00207 
00208             list_remove( &stream->entry );
00209             IStream_Release( stream->stm );
00210             IStream_Release( stream->stg );
00211             IStorage_DestroyElement( stream->stg, stname );
00212             msi_free( stream );
00213             CoTaskMemFree( stat.pwcsName );
00214             break;
00215         }
00216         CoTaskMemFree( stat.pwcsName );
00217     }
00218 }
00219 
00220 static void free_streams( MSIDATABASE *db )
00221 {
00222     while( !list_empty( &db->streams ) )
00223     {
00224         MSISTREAM *s = LIST_ENTRY(list_head( &db->streams ), MSISTREAM, entry);
00225         list_remove( &s->entry );
00226         IStream_Release( s->stm );
00227         IStream_Release( s->stg );
00228         msi_free( s );
00229     }
00230 }
00231 
00232 void append_storage_to_db( MSIDATABASE *db, IStorage *stg )
00233 {
00234     MSITRANSFORM *t;
00235 
00236     t = msi_alloc( sizeof *t );
00237     t->stg = stg;
00238     IStorage_AddRef( stg );
00239     list_add_head( &db->transforms, &t->entry );
00240 
00241     /* the transform may add or replace streams */
00242     free_streams( db );
00243 }
00244 
00245 static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
00246 {
00247     MSIDATABASE *db = (MSIDATABASE *) arg;
00248 
00249     msi_free(db->path);
00250     free_cached_tables( db );
00251     free_streams( db );
00252     free_transforms( db );
00253     if (db->strings) msi_destroy_stringtable( db->strings );
00254     IStorage_Release( db->storage );
00255     if (db->deletefile)
00256     {
00257         DeleteFileW( db->deletefile );
00258         msi_free( db->deletefile );
00259     }
00260 }
00261 
00262 static HRESULT db_initialize( IStorage *stg, const GUID *clsid )
00263 {
00264     static const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
00265     HRESULT hr;
00266 
00267     hr = IStorage_SetClass( stg, clsid );
00268     if (FAILED( hr ))
00269     {
00270         WARN("failed to set class id 0x%08x\n", hr);
00271         return hr;
00272     }
00273 
00274     /* create the _Tables stream */
00275     hr = write_stream_data( stg, szTables, NULL, 0, TRUE );
00276     if (FAILED( hr ))
00277     {
00278         WARN("failed to create _Tables stream 0x%08x\n", hr);
00279         return hr;
00280     }
00281 
00282     hr = msi_init_string_table( stg );
00283     if (FAILED( hr ))
00284     {
00285         WARN("failed to initialize string table 0x%08x\n", hr);
00286         return hr;
00287     }
00288 
00289     hr = IStorage_Commit( stg, 0 );
00290     if (FAILED( hr ))
00291     {
00292         WARN("failed to commit changes 0x%08x\n", hr);
00293         return hr;
00294     }
00295 
00296     return S_OK;
00297 }
00298 
00299 UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
00300 {
00301     IStorage *stg = NULL;
00302     HRESULT r;
00303     MSIDATABASE *db = NULL;
00304     UINT ret = ERROR_FUNCTION_FAILED;
00305     LPCWSTR szMode, save_path;
00306     STATSTG stat;
00307     BOOL created = FALSE, patch = FALSE;
00308     WCHAR path[MAX_PATH];
00309 
00310     TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
00311 
00312     if( !pdb )
00313         return ERROR_INVALID_PARAMETER;
00314 
00315     if (szPersist - MSIDBOPEN_PATCHFILE >= MSIDBOPEN_READONLY &&
00316         szPersist - MSIDBOPEN_PATCHFILE <= MSIDBOPEN_CREATEDIRECT)
00317     {
00318         TRACE("Database is a patch\n");
00319         szPersist -= MSIDBOPEN_PATCHFILE;
00320         patch = TRUE;
00321     }
00322 
00323     save_path = szDBPath;
00324     szMode = szPersist;
00325     if( !IS_INTMSIDBOPEN(szPersist) )
00326     {
00327         if (!CopyFileW( szDBPath, szPersist, FALSE ))
00328             return ERROR_OPEN_FAILED;
00329 
00330         szDBPath = szPersist;
00331         szPersist = MSIDBOPEN_TRANSACT;
00332         created = TRUE;
00333     }
00334 
00335     if( szPersist == MSIDBOPEN_READONLY )
00336     {
00337         r = StgOpenStorage( szDBPath, NULL,
00338               STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
00339     }
00340     else if( szPersist == MSIDBOPEN_CREATE )
00341     {
00342         r = StgCreateDocfile( szDBPath,
00343               STGM_CREATE|STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg );
00344 
00345         if( SUCCEEDED(r) )
00346             r = db_initialize( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase );
00347         created = TRUE;
00348     }
00349     else if( szPersist == MSIDBOPEN_CREATEDIRECT )
00350     {
00351         r = StgCreateDocfile( szDBPath,
00352               STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg );
00353 
00354         if( SUCCEEDED(r) )
00355             r = db_initialize( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase );
00356         created = TRUE;
00357     }
00358     else if( szPersist == MSIDBOPEN_TRANSACT )
00359     {
00360         r = StgOpenStorage( szDBPath, NULL,
00361               STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
00362     }
00363     else if( szPersist == MSIDBOPEN_DIRECT )
00364     {
00365         r = StgOpenStorage( szDBPath, NULL,
00366               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
00367     }
00368     else
00369     {
00370         ERR("unknown flag %p\n",szPersist);
00371         return ERROR_INVALID_PARAMETER;
00372     }
00373 
00374     if( FAILED( r ) || !stg )
00375     {
00376         WARN("open failed r = %08x for %s\n", r, debugstr_w(szDBPath));
00377         return ERROR_FUNCTION_FAILED;
00378     }
00379 
00380     r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
00381     if( FAILED( r ) )
00382     {
00383         FIXME("Failed to stat storage\n");
00384         goto end;
00385     }
00386 
00387     if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
00388          !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) &&
00389          !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
00390     {
00391         ERR("storage GUID is not a MSI database GUID %s\n",
00392              debugstr_guid(&stat.clsid) );
00393         goto end;
00394     }
00395 
00396     if ( patch && !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) )
00397     {
00398         ERR("storage GUID is not the MSI patch GUID %s\n",
00399              debugstr_guid(&stat.clsid) );
00400         ret = ERROR_OPEN_FAILED;
00401         goto end;
00402     }
00403 
00404     db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
00405                               MSI_CloseDatabase );
00406     if( !db )
00407     {
00408         FIXME("Failed to allocate a handle\n");
00409         goto end;
00410     }
00411 
00412     if (!strchrW( save_path, '\\' ))
00413     {
00414         GetCurrentDirectoryW( MAX_PATH, path );
00415         lstrcatW( path, szBackSlash );
00416         lstrcatW( path, save_path );
00417     }
00418     else
00419         lstrcpyW( path, save_path );
00420 
00421     db->path = strdupW( path );
00422     db->media_transform_offset = MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
00423     db->media_transform_disk_id = MSI_INITIAL_MEDIA_TRANSFORM_DISKID;
00424 
00425     if( TRACE_ON( msi ) )
00426         enum_stream_names( stg );
00427 
00428     db->storage = stg;
00429     db->mode = szMode;
00430     if (created)
00431         db->deletefile = strdupW( szDBPath );
00432     list_init( &db->tables );
00433     list_init( &db->transforms );
00434     list_init( &db->streams );
00435 
00436     db->strings = msi_load_string_table( stg, &db->bytes_per_strref );
00437     if( !db->strings )
00438         goto end;
00439 
00440     ret = ERROR_SUCCESS;
00441 
00442     msiobj_addref( &db->hdr );
00443     IStorage_AddRef( stg );
00444     *pdb = db;
00445 
00446 end:
00447     if( db )
00448         msiobj_release( &db->hdr );
00449     if( stg )
00450         IStorage_Release( stg );
00451 
00452     return ret;
00453 }
00454 
00455 UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
00456 {
00457     MSIDATABASE *db;
00458     UINT ret;
00459 
00460     TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
00461 
00462     ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
00463     if( ret == ERROR_SUCCESS )
00464     {
00465         *phDB = alloc_msihandle( &db->hdr );
00466         if (! *phDB)
00467             ret = ERROR_NOT_ENOUGH_MEMORY;
00468         msiobj_release( &db->hdr );
00469     }
00470 
00471     return ret;
00472 }
00473 
00474 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
00475 {
00476     HRESULT r = ERROR_FUNCTION_FAILED;
00477     LPWSTR szwDBPath = NULL, szwPersist = NULL;
00478 
00479     TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
00480 
00481     if( szDBPath )
00482     {
00483         szwDBPath = strdupAtoW( szDBPath );
00484         if( !szwDBPath )
00485             goto end;
00486     }
00487 
00488     if( !IS_INTMSIDBOPEN(szPersist) )
00489     {
00490         szwPersist = strdupAtoW( szPersist );
00491         if( !szwPersist )
00492             goto end;
00493     }
00494     else
00495         szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
00496 
00497     r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
00498 
00499 end:
00500     if( !IS_INTMSIDBOPEN(szPersist) )
00501         msi_free( szwPersist );
00502     msi_free( szwDBPath );
00503 
00504     return r;
00505 }
00506 
00507 static LPWSTR msi_read_text_archive(LPCWSTR path, DWORD *len)
00508 {
00509     HANDLE file;
00510     LPSTR data = NULL;
00511     LPWSTR wdata = NULL;
00512     DWORD read, size = 0;
00513 
00514     file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
00515     if (file == INVALID_HANDLE_VALUE)
00516         return NULL;
00517 
00518     size = GetFileSize( file, NULL );
00519     if (!(data = msi_alloc( size ))) goto done;
00520 
00521     if (!ReadFile( file, data, size, &read, NULL ) || read != size) goto done;
00522 
00523     while (!data[size - 1]) size--;
00524     *len = MultiByteToWideChar( CP_ACP, 0, data, size, NULL, 0 );
00525     if ((wdata = msi_alloc( (*len + 1) * sizeof(WCHAR) )))
00526     {
00527         MultiByteToWideChar( CP_ACP, 0, data, size, wdata, *len );
00528         wdata[*len] = 0;
00529     }
00530 
00531 done:
00532     CloseHandle( file );
00533     msi_free( data );
00534     return wdata;
00535 }
00536 
00537 static void msi_parse_line(LPWSTR *line, LPWSTR **entries, DWORD *num_entries, DWORD *len)
00538 {
00539     LPWSTR ptr = *line, save;
00540     DWORD i, count = 1, chars_left = *len;
00541 
00542     *entries = NULL;
00543 
00544     /* stay on this line */
00545     while (chars_left && *ptr != '\n')
00546     {
00547         /* entries are separated by tabs */
00548         if (*ptr == '\t')
00549             count++;
00550 
00551         ptr++;
00552         chars_left--;
00553     }
00554 
00555     *entries = msi_alloc(count * sizeof(LPWSTR));
00556     if (!*entries)
00557         return;
00558 
00559     /* store pointers into the data */
00560     chars_left = *len;
00561     for (i = 0, ptr = *line; i < count; i++)
00562     {
00563         while (chars_left && *ptr == '\r')
00564         {
00565             ptr++;
00566             chars_left--;
00567         }
00568         save = ptr;
00569 
00570         while (chars_left && *ptr != '\t' && *ptr != '\n' && *ptr != '\r')
00571         {
00572             if (!*ptr) *ptr = '\n'; /* convert embedded nulls to \n */
00573             if (ptr > *line && *ptr == '\x19' && *(ptr - 1) == '\x11')
00574             {
00575                 *ptr = '\n';
00576                 *(ptr - 1) = '\r';
00577             }
00578             ptr++;
00579             chars_left--;
00580         }
00581 
00582         /* NULL-separate the data */
00583         if (*ptr == '\n' || *ptr == '\r')
00584         {
00585             while (chars_left && (*ptr == '\n' || *ptr == '\r'))
00586             {
00587                 *(ptr++) = 0;
00588                 chars_left--;
00589             }
00590         }
00591         else if (*ptr)
00592         {
00593             *(ptr++) = 0;
00594             chars_left--;
00595         }
00596         (*entries)[i] = save;
00597     }
00598 
00599     /* move to the next line if there's more, else EOF */
00600     *line = ptr;
00601     *len = chars_left;
00602     if (num_entries)
00603         *num_entries = count;
00604 }
00605 
00606 static LPWSTR msi_build_createsql_prelude(LPWSTR table)
00607 {
00608     LPWSTR prelude;
00609     DWORD size;
00610 
00611     static const WCHAR create_fmt[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
00612 
00613     size = sizeof(create_fmt)/sizeof(create_fmt[0]) + lstrlenW(table) - 2;
00614     prelude = msi_alloc(size * sizeof(WCHAR));
00615     if (!prelude)
00616         return NULL;
00617 
00618     sprintfW(prelude, create_fmt, table);
00619     return prelude;
00620 }
00621 
00622 static LPWSTR msi_build_createsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns)
00623 {
00624     LPWSTR columns, p;
00625     LPCWSTR type;
00626     DWORD sql_size = 1, i, len;
00627     WCHAR expanded[128], *ptr;
00628     WCHAR size[10], comma[2], extra[30];
00629 
00630     static const WCHAR column_fmt[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
00631     static const WCHAR size_fmt[] = {'(','%','s',')',0};
00632     static const WCHAR type_char[] = {'C','H','A','R',0};
00633     static const WCHAR type_int[] = {'I','N','T',0};
00634     static const WCHAR type_long[] = {'L','O','N','G',0};
00635     static const WCHAR type_object[] = {'O','B','J','E','C','T',0};
00636     static const WCHAR type_notnull[] = {' ','N','O','T',' ','N','U','L','L',0};
00637     static const WCHAR localizable[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0};
00638 
00639     columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
00640     if (!columns)
00641         return NULL;
00642 
00643     for (i = 0; i < num_columns; i++)
00644     {
00645         type = NULL;
00646         comma[1] = size[0] = extra[0] = '\0';
00647 
00648         if (i == num_columns - 1)
00649             comma[0] = '\0';
00650         else
00651             comma[0] = ',';
00652 
00653         ptr = &types[i][1];
00654         len = atolW(ptr);
00655         extra[0] = '\0';
00656 
00657         switch (types[i][0])
00658         {
00659             case 'l':
00660                 lstrcpyW(extra, type_notnull);
00661                 /* fall through */
00662             case 'L':
00663                 lstrcatW(extra, localizable);
00664                 type = type_char;
00665                 sprintfW(size, size_fmt, ptr);
00666                 break;
00667             case 's':
00668                 lstrcpyW(extra, type_notnull);
00669                 /* fall through */
00670             case 'S':
00671                 type = type_char;
00672                 sprintfW(size, size_fmt, ptr);
00673                 break;
00674             case 'i':
00675                 lstrcpyW(extra, type_notnull);
00676                 /* fall through */
00677             case 'I':
00678                 if (len <= 2)
00679                     type = type_int;
00680                 else if (len == 4)
00681                     type = type_long;
00682                 else
00683                 {
00684                     WARN("invalid int width %u\n", len);
00685                     msi_free(columns);
00686                     return NULL;
00687                 }
00688                 break;
00689             case 'v':
00690                 lstrcpyW(extra, type_notnull);
00691                 /* fall through */
00692             case 'V':
00693                 type = type_object;
00694                 break;
00695             default:
00696                 ERR("Unknown type: %c\n", types[i][0]);
00697                 msi_free(columns);
00698                 return NULL;
00699         }
00700 
00701         sprintfW(expanded, column_fmt, columns_data[i], type, size, extra, comma);
00702         sql_size += lstrlenW(expanded);
00703 
00704         p = msi_realloc(columns, sql_size * sizeof(WCHAR));
00705         if (!p)
00706         {
00707             msi_free(columns);
00708             return NULL;
00709         }
00710         columns = p;
00711 
00712         lstrcatW(columns, expanded);
00713     }
00714 
00715     return columns;
00716 }
00717 
00718 static LPWSTR msi_build_createsql_postlude(LPWSTR *primary_keys, DWORD num_keys)
00719 {
00720     LPWSTR postlude, keys, ptr;
00721     DWORD size, key_size, i;
00722 
00723     static const WCHAR key_fmt[] = {'`','%','s','`',',',' ',0};
00724     static const WCHAR postlude_fmt[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0};
00725 
00726     for (i = 0, size = 1; i < num_keys; i++)
00727         size += lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) - 2;
00728 
00729     keys = msi_alloc(size * sizeof(WCHAR));
00730     if (!keys)
00731         return NULL;
00732 
00733     for (i = 0, ptr = keys; i < num_keys; i++)
00734     {
00735         key_size = lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) -2;
00736         sprintfW(ptr, key_fmt, primary_keys[i]);
00737         ptr += key_size;
00738     }
00739 
00740     /* remove final ', ' */
00741     *(ptr - 2) = '\0';
00742 
00743     size = lstrlenW(postlude_fmt) + size - 1;
00744     postlude = msi_alloc(size * sizeof(WCHAR));
00745     if (!postlude)
00746         goto done;
00747 
00748     sprintfW(postlude, postlude_fmt, keys);
00749 
00750 done:
00751     msi_free(keys);
00752     return postlude;
00753 }
00754 
00755 static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_labels, DWORD num_columns)
00756 {
00757     UINT r = ERROR_OUTOFMEMORY;
00758     DWORD size;
00759     MSIQUERY *view;
00760     LPWSTR create_sql = NULL;
00761     LPWSTR prelude, columns_sql, postlude;
00762 
00763     prelude = msi_build_createsql_prelude(labels[0]);
00764     columns_sql = msi_build_createsql_columns(columns, types, num_columns);
00765     postlude = msi_build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */
00766 
00767     if (!prelude || !columns_sql || !postlude)
00768         goto done;
00769 
00770     size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
00771     create_sql = msi_alloc(size * sizeof(WCHAR));
00772     if (!create_sql)
00773         goto done;
00774 
00775     lstrcpyW(create_sql, prelude);
00776     lstrcatW(create_sql, columns_sql);
00777     lstrcatW(create_sql, postlude);
00778 
00779     r = MSI_DatabaseOpenViewW( db, create_sql, &view );
00780     if (r != ERROR_SUCCESS)
00781         goto done;
00782 
00783     r = MSI_ViewExecute(view, NULL);
00784     MSI_ViewClose(view);
00785     msiobj_release(&view->hdr);
00786 
00787 done:
00788     msi_free(prelude);
00789     msi_free(columns_sql);
00790     msi_free(postlude);
00791     msi_free(create_sql);
00792     return r;
00793 }
00794 
00795 static LPWSTR msi_import_stream_filename(LPCWSTR path, LPCWSTR name)
00796 {
00797     DWORD len;
00798     LPWSTR fullname, ptr;
00799 
00800     len = lstrlenW(path) + lstrlenW(name) + 1;
00801     fullname = msi_alloc(len*sizeof(WCHAR));
00802     if (!fullname)
00803        return NULL;
00804 
00805     lstrcpyW( fullname, path );
00806 
00807     /* chop off extension from path */
00808     ptr = strrchrW(fullname, '.');
00809     if (!ptr)
00810     {
00811         msi_free (fullname);
00812         return NULL;
00813     }
00814     *ptr++ = '\\';
00815     lstrcpyW( ptr, name );
00816     return fullname;
00817 }
00818 
00819 static UINT construct_record(DWORD num_columns, LPWSTR *types,
00820                              LPWSTR *data, LPWSTR path, MSIRECORD **rec)
00821 {
00822     UINT i;
00823 
00824     *rec = MSI_CreateRecord(num_columns);
00825     if (!*rec)
00826         return ERROR_OUTOFMEMORY;
00827 
00828     for (i = 0; i < num_columns; i++)
00829     {
00830         switch (types[i][0])
00831         {
00832             case 'L': case 'l': case 'S': case 's':
00833                 MSI_RecordSetStringW(*rec, i + 1, data[i]);
00834                 break;
00835             case 'I': case 'i':
00836                 if (*data[i])
00837                     MSI_RecordSetInteger(*rec, i + 1, atoiW(data[i]));
00838                 break;
00839             case 'V': case 'v':
00840                 if (*data[i])
00841                 {
00842                     UINT r;
00843                     LPWSTR file = msi_import_stream_filename(path, data[i]);
00844                     if (!file)
00845                         return ERROR_FUNCTION_FAILED;
00846 
00847                     r = MSI_RecordSetStreamFromFileW(*rec, i + 1, file);
00848                     msi_free (file);
00849                     if (r != ERROR_SUCCESS)
00850                         return ERROR_FUNCTION_FAILED;
00851                 }
00852                 break;
00853             default:
00854                 ERR("Unhandled column type: %c\n", types[i][0]);
00855                 msiobj_release(&(*rec)->hdr);
00856                 return ERROR_FUNCTION_FAILED;
00857         }
00858     }
00859 
00860     return ERROR_SUCCESS;
00861 }
00862 
00863 static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types,
00864                                      LPWSTR *labels, LPWSTR **records,
00865                                      int num_columns, int num_records,
00866                                      LPWSTR path)
00867 {
00868     UINT r;
00869     int i;
00870     MSIQUERY *view;
00871     MSIRECORD *rec;
00872 
00873     static const WCHAR select[] = {
00874         'S','E','L','E','C','T',' ','*',' ',
00875         'F','R','O','M',' ','`','%','s','`',0
00876     };
00877 
00878     r = MSI_OpenQuery(db, &view, select, labels[0]);
00879     if (r != ERROR_SUCCESS)
00880         return r;
00881 
00882     while (MSI_ViewFetch(view, &rec) != ERROR_NO_MORE_ITEMS)
00883     {
00884         r = MSI_ViewModify(view, MSIMODIFY_DELETE, rec);
00885         msiobj_release(&rec->hdr);
00886         if (r != ERROR_SUCCESS)
00887             goto done;
00888     }
00889 
00890     for (i = 0; i < num_records; i++)
00891     {
00892         r = construct_record(num_columns, types, records[i], path, &rec);
00893         if (r != ERROR_SUCCESS)
00894             goto done;
00895 
00896         r = MSI_ViewModify(view, MSIMODIFY_INSERT, rec);
00897         if (r != ERROR_SUCCESS)
00898         {
00899             msiobj_release(&rec->hdr);
00900             goto done;
00901         }
00902 
00903         msiobj_release(&rec->hdr);
00904     }
00905 
00906 done:
00907     msiobj_release(&view->hdr);
00908     return r;
00909 }
00910 
00911 static UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
00912 {
00913     UINT r;
00914     DWORD len, i;
00915     DWORD num_labels, num_types;
00916     DWORD num_columns, num_records = 0;
00917     LPWSTR *columns, *types, *labels;
00918     LPWSTR path, ptr, data;
00919     LPWSTR **records = NULL;
00920     LPWSTR **temp_records;
00921 
00922     static const WCHAR suminfo[] =
00923         {'_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0};
00924     static const WCHAR forcecodepage[] =
00925         {'_','F','o','r','c','e','C','o','d','e','p','a','g','e',0};
00926 
00927     TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
00928 
00929     if( folder == NULL || file == NULL )
00930         return ERROR_INVALID_PARAMETER;
00931 
00932     len = lstrlenW(folder) + lstrlenW(szBackSlash) + lstrlenW(file) + 1;
00933     path = msi_alloc( len * sizeof(WCHAR) );
00934     if (!path)
00935         return ERROR_OUTOFMEMORY;
00936 
00937     lstrcpyW( path, folder );
00938     lstrcatW( path, szBackSlash );
00939     lstrcatW( path, file );
00940 
00941     data = msi_read_text_archive( path, &len );
00942 
00943     ptr = data;
00944     msi_parse_line( &ptr, &columns, &num_columns, &len );
00945     msi_parse_line( &ptr, &types, &num_types, &len );
00946     msi_parse_line( &ptr, &labels, &num_labels, &len );
00947 
00948     if (num_columns == 1 && !columns[0][0] && num_labels == 1 && !labels[0][0] &&
00949         num_types == 2 && !strcmpW( types[1], forcecodepage ))
00950     {
00951         r = msi_set_string_table_codepage( db->strings, atoiW( types[0] ) );
00952         goto done;
00953     }
00954 
00955     if (num_columns != num_types)
00956     {
00957         r = ERROR_FUNCTION_FAILED;
00958         goto done;
00959     }
00960 
00961     records = msi_alloc(sizeof(LPWSTR *));
00962     if (!records)
00963     {
00964         r = ERROR_OUTOFMEMORY;
00965         goto done;
00966     }
00967 
00968     /* read in the table records */
00969     while (len)
00970     {
00971         msi_parse_line( &ptr, &records[num_records], NULL, &len );
00972 
00973         num_records++;
00974         temp_records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *));
00975         if (!temp_records)
00976         {
00977             r = ERROR_OUTOFMEMORY;
00978             goto done;
00979         }
00980         records = temp_records;
00981     }
00982 
00983     if (!strcmpW(labels[0], suminfo))
00984     {
00985         r = msi_add_suminfo( db, records, num_records, num_columns );
00986         if (r != ERROR_SUCCESS)
00987         {
00988             r = ERROR_FUNCTION_FAILED;
00989             goto done;
00990         }
00991     }
00992     else
00993     {
00994         if (!TABLE_Exists(db, labels[0]))
00995         {
00996             r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns );
00997             if (r != ERROR_SUCCESS)
00998             {
00999                 r = ERROR_FUNCTION_FAILED;
01000                 goto done;
01001             }
01002         }
01003 
01004         r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records, path );
01005     }
01006 
01007 done:
01008     msi_free(path);
01009     msi_free(data);
01010     msi_free(columns);
01011     msi_free(types);
01012     msi_free(labels);
01013 
01014     for (i = 0; i < num_records; i++)
01015         msi_free(records[i]);
01016 
01017     msi_free(records);
01018 
01019     return r;
01020 }
01021 
01022 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
01023 {
01024     MSIDATABASE *db;
01025     UINT r;
01026 
01027     TRACE("%x %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
01028 
01029     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
01030     if( !db )
01031     {
01032         IWineMsiRemoteDatabase *remote_database;
01033 
01034         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
01035         if ( !remote_database )
01036             return ERROR_INVALID_HANDLE;
01037 
01038         IWineMsiRemoteDatabase_Release( remote_database );
01039         WARN("MsiDatabaseImport not allowed during a custom action!\n");
01040 
01041         return ERROR_SUCCESS;
01042     }
01043 
01044     r = MSI_DatabaseImport( db, szFolder, szFilename );
01045     msiobj_release( &db->hdr );
01046     return r;
01047 }
01048 
01049 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
01050                LPCSTR szFolder, LPCSTR szFilename )
01051 {
01052     LPWSTR path = NULL, file = NULL;
01053     UINT r = ERROR_OUTOFMEMORY;
01054 
01055     TRACE("%x %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
01056 
01057     if( szFolder )
01058     {
01059         path = strdupAtoW( szFolder );
01060         if( !path )
01061             goto end;
01062     }
01063 
01064     if( szFilename )
01065     {
01066         file = strdupAtoW( szFilename );
01067         if( !file )
01068             goto end;
01069     }
01070 
01071     r = MsiDatabaseImportW( handle, path, file );
01072 
01073 end:
01074     msi_free( path );
01075     msi_free( file );
01076 
01077     return r;
01078 }
01079 
01080 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
01081 {
01082     UINT i, count, len, r = ERROR_SUCCESS;
01083     const char *sep;
01084     char *buffer;
01085     DWORD sz;
01086 
01087     len = 0x100;
01088     buffer = msi_alloc( len );
01089     if ( !buffer )
01090         return ERROR_OUTOFMEMORY;
01091 
01092     count = MSI_RecordGetFieldCount( row );
01093     for ( i=start; i<=count; i++ )
01094     {
01095         sz = len;
01096         r = MSI_RecordGetStringA( row, i, buffer, &sz );
01097         if (r == ERROR_MORE_DATA)
01098         {
01099             char *p = msi_realloc( buffer, sz + 1 );
01100             if (!p)
01101                 break;
01102             len = sz + 1;
01103             buffer = p;
01104         }
01105         sz = len;
01106         r = MSI_RecordGetStringA( row, i, buffer, &sz );
01107         if (r != ERROR_SUCCESS)
01108             break;
01109 
01110         if (!WriteFile( handle, buffer, sz, &sz, NULL ))
01111         {
01112             r = ERROR_FUNCTION_FAILED;
01113             break;
01114         }
01115 
01116         sep = (i < count) ? "\t" : "\r\n";
01117         if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
01118         {
01119             r = ERROR_FUNCTION_FAILED;
01120             break;
01121         }
01122     }
01123     msi_free( buffer );
01124     return r;
01125 }
01126 
01127 static UINT msi_export_row( MSIRECORD *row, void *arg )
01128 {
01129     return msi_export_record( arg, row, 1 );
01130 }
01131 
01132 static UINT msi_export_forcecodepage( HANDLE handle, UINT codepage )
01133 {
01134     static const char fmt[] = "\r\n\r\n%u\t_ForceCodepage\r\n";
01135     char data[sizeof(fmt) + 10];
01136     DWORD sz;
01137 
01138     sprintf( data, fmt, codepage );
01139 
01140     sz = lstrlenA(data) + 1;
01141     if (!WriteFile(handle, data, sz, &sz, NULL))
01142         return ERROR_FUNCTION_FAILED;
01143 
01144     return ERROR_SUCCESS;
01145 }
01146 
01147 static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
01148                LPCWSTR folder, LPCWSTR file )
01149 {
01150     static const WCHAR query[] = {
01151         's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
01152     static const WCHAR forcecodepage[] = {
01153         '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 };
01154     MSIRECORD *rec = NULL;
01155     MSIQUERY *view = NULL;
01156     LPWSTR filename;
01157     HANDLE handle;
01158     UINT len, r;
01159 
01160     TRACE("%p %s %s %s\n", db, debugstr_w(table),
01161           debugstr_w(folder), debugstr_w(file) );
01162 
01163     if( folder == NULL || file == NULL )
01164         return ERROR_INVALID_PARAMETER;
01165 
01166     len = lstrlenW(folder) + lstrlenW(file) + 2;
01167     filename = msi_alloc(len * sizeof (WCHAR));
01168     if (!filename)
01169         return ERROR_OUTOFMEMORY;
01170 
01171     lstrcpyW( filename, folder );
01172     lstrcatW( filename, szBackSlash );
01173     lstrcatW( filename, file );
01174 
01175     handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
01176                           NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
01177     msi_free( filename );
01178     if (handle == INVALID_HANDLE_VALUE)
01179         return ERROR_FUNCTION_FAILED;
01180 
01181     if (!strcmpW( table, forcecodepage ))
01182     {
01183         UINT codepage = msi_get_string_table_codepage( db->strings );
01184         r = msi_export_forcecodepage( handle, codepage );
01185         goto done;
01186     }
01187 
01188     r = MSI_OpenQuery( db, &view, query, table );
01189     if (r == ERROR_SUCCESS)
01190     {
01191         /* write out row 1, the column names */
01192         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
01193         if (r == ERROR_SUCCESS)
01194         {
01195             msi_export_record( handle, rec, 1 );
01196             msiobj_release( &rec->hdr );
01197         }
01198 
01199         /* write out row 2, the column types */
01200         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
01201         if (r == ERROR_SUCCESS)
01202         {
01203             msi_export_record( handle, rec, 1 );
01204             msiobj_release( &rec->hdr );
01205         }
01206 
01207         /* write out row 3, the table name + keys */
01208         r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
01209         if (r == ERROR_SUCCESS)
01210         {
01211             MSI_RecordSetStringW( rec, 0, table );
01212             msi_export_record( handle, rec, 0 );
01213             msiobj_release( &rec->hdr );
01214         }
01215 
01216         /* write out row 4 onwards, the data */
01217         r = MSI_IterateRecords( view, 0, msi_export_row, handle );
01218         msiobj_release( &view->hdr );
01219     }
01220 
01221 done:
01222     CloseHandle( handle );
01223     return r;
01224 }
01225 
01226 /***********************************************************************
01227  * MsiExportDatabaseW        [MSI.@]
01228  *
01229  * Writes a file containing the table data as tab separated ASCII.
01230  *
01231  * The format is as follows:
01232  *
01233  * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
01234  * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
01235  * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
01236  *
01237  * Followed by the data, starting at row 1 with one row per line
01238  *
01239  * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
01240  */
01241 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
01242                LPCWSTR szFolder, LPCWSTR szFilename )
01243 {
01244     MSIDATABASE *db;
01245     UINT r;
01246 
01247     TRACE("%x %s %s %s\n", handle, debugstr_w(szTable),
01248           debugstr_w(szFolder), debugstr_w(szFilename));
01249 
01250     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
01251     if( !db )
01252     {
01253         IWineMsiRemoteDatabase *remote_database;
01254 
01255         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
01256         if ( !remote_database )
01257             return ERROR_INVALID_HANDLE;
01258 
01259         IWineMsiRemoteDatabase_Release( remote_database );
01260         WARN("MsiDatabaseExport not allowed during a custom action!\n");
01261 
01262         return ERROR_SUCCESS;
01263     }
01264 
01265     r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
01266     msiobj_release( &db->hdr );
01267     return r;
01268 }
01269 
01270 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
01271                LPCSTR szFolder, LPCSTR szFilename )
01272 {
01273     LPWSTR path = NULL, file = NULL, table = NULL;
01274     UINT r = ERROR_OUTOFMEMORY;
01275 
01276     TRACE("%x %s %s %s\n", handle, debugstr_a(szTable),
01277           debugstr_a(szFolder), debugstr_a(szFilename));
01278 
01279     if( szTable )
01280     {
01281         table = strdupAtoW( szTable );
01282         if( !table )
01283             goto end;
01284     }
01285 
01286     if( szFolder )
01287     {
01288         path = strdupAtoW( szFolder );
01289         if( !path )
01290             goto end;
01291     }
01292 
01293     if( szFilename )
01294     {
01295         file = strdupAtoW( szFilename );
01296         if( !file )
01297             goto end;
01298     }
01299 
01300     r = MsiDatabaseExportW( handle, table, path, file );
01301 
01302 end:
01303     msi_free( table );
01304     msi_free( path );
01305     msi_free( file );
01306 
01307     return r;
01308 }
01309 
01310 UINT WINAPI MsiDatabaseMergeA(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
01311                               LPCSTR szTableName)
01312 {
01313     UINT r;
01314     LPWSTR table;
01315 
01316     TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
01317           debugstr_a(szTableName));
01318 
01319     table = strdupAtoW(szTableName);
01320     r = MsiDatabaseMergeW(hDatabase, hDatabaseMerge, table);
01321 
01322     msi_free(table);
01323     return r;
01324 }
01325 
01326 typedef struct _tagMERGETABLE
01327 {
01328     struct list entry;
01329     struct list rows;
01330     LPWSTR name;
01331     DWORD numconflicts;
01332     LPWSTR *columns;
01333     DWORD numcolumns;
01334     LPWSTR *types;
01335     DWORD numtypes;
01336     LPWSTR *labels;
01337     DWORD numlabels;
01338 } MERGETABLE;
01339 
01340 typedef struct _tagMERGEROW
01341 {
01342     struct list entry;
01343     MSIRECORD *data;
01344 } MERGEROW;
01345 
01346 typedef struct _tagMERGEDATA
01347 {
01348     MSIDATABASE *db;
01349     MSIDATABASE *merge;
01350     MERGETABLE *curtable;
01351     MSIQUERY *curview;
01352     struct list *tabledata;
01353 } MERGEDATA;
01354 
01355 static BOOL merge_type_match(LPCWSTR type1, LPCWSTR type2)
01356 {
01357     if (((type1[0] == 'l') || (type1[0] == 's')) &&
01358         ((type2[0] == 'l') || (type2[0] == 's')))
01359         return TRUE;
01360 
01361     if (((type1[0] == 'L') || (type1[0] == 'S')) &&
01362         ((type2[0] == 'L') || (type2[0] == 'S')))
01363         return TRUE;
01364 
01365     return !strcmpW( type1, type2 );
01366 }
01367 
01368 static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview)
01369 {
01370     MSIRECORD *dbrec, *mergerec;
01371     UINT r, i, count;
01372 
01373     r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec);
01374     if (r != ERROR_SUCCESS)
01375         return r;
01376 
01377     r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec);
01378     if (r != ERROR_SUCCESS)
01379         return r;
01380 
01381     count = MSI_RecordGetFieldCount(dbrec);
01382     for (i = 1; i <= count; i++)
01383     {
01384         if (!MSI_RecordGetString(mergerec, i))
01385             break;
01386 
01387         if (strcmpW( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) ))
01388         {
01389             r = ERROR_DATATYPE_MISMATCH;
01390             goto done;
01391         }
01392     }
01393 
01394     msiobj_release(&dbrec->hdr);
01395     msiobj_release(&mergerec->hdr);
01396     dbrec = mergerec = NULL;
01397 
01398     r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec);
01399     if (r != ERROR_SUCCESS)
01400         return r;
01401 
01402     r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec);
01403     if (r != ERROR_SUCCESS)
01404         return r;
01405 
01406     count = MSI_RecordGetFieldCount(dbrec);
01407     for (i = 1; i <= count; i++)
01408     {
01409         if (!MSI_RecordGetString(mergerec, i))
01410             break;
01411 
01412         if (!merge_type_match(MSI_RecordGetString(dbrec, i),
01413                      MSI_RecordGetString(mergerec, i)))
01414         {
01415             r = ERROR_DATATYPE_MISMATCH;
01416             break;
01417         }
01418     }
01419 
01420 done:
01421     msiobj_release(&dbrec->hdr);
01422     msiobj_release(&mergerec->hdr);
01423 
01424     return r;
01425 }
01426 
01427 static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb,
01428                                       LPCWSTR table)
01429 {
01430     MSIRECORD *dbrec, *mergerec = NULL;
01431     UINT r, i, count;
01432 
01433     r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec);
01434     if (r != ERROR_SUCCESS)
01435         return r;
01436 
01437     r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec);
01438     if (r != ERROR_SUCCESS)
01439         goto done;
01440 
01441     count = MSI_RecordGetFieldCount(dbrec);
01442     if (count != MSI_RecordGetFieldCount(mergerec))
01443     {
01444         r = ERROR_DATATYPE_MISMATCH;
01445         goto done;
01446     }
01447 
01448     for (i = 1; i <= count; i++)
01449     {
01450         if (strcmpW( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) ))
01451         {
01452             r = ERROR_DATATYPE_MISMATCH;
01453             goto done;
01454         }
01455     }
01456 
01457 done:
01458     msiobj_release(&dbrec->hdr);
01459     msiobj_release(&mergerec->hdr);
01460 
01461     return r;
01462 }
01463 
01464 static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec)
01465 {
01466     MSIRECORD *colnames;
01467     LPWSTR str, val;
01468     UINT r, i = 0, sz = 0;
01469     int cmp;
01470 
01471     r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames);
01472     if (r != ERROR_SUCCESS)
01473         return NULL;
01474 
01475     do
01476     {
01477         str = msi_dup_record_field(colnames, ++i);
01478         cmp = strcmpW( key, str );
01479         msi_free(str);
01480     } while (cmp);
01481 
01482     msiobj_release(&colnames->hdr);
01483 
01484     r = MSI_RecordGetStringW(rec, i, NULL, &sz);
01485     if (r != ERROR_SUCCESS)
01486         return NULL;
01487     sz++;
01488 
01489     if (MSI_RecordGetString(rec, i))  /* check record field is a string */
01490     {
01491         /* quote string record fields */
01492         const WCHAR szQuote[] = {'\'', 0};
01493         sz += 2;
01494         val = msi_alloc(sz*sizeof(WCHAR));
01495         if (!val)
01496             return NULL;
01497 
01498         lstrcpyW(val, szQuote);
01499         r = MSI_RecordGetStringW(rec, i, val+1, &sz);
01500         lstrcpyW(val+1+sz, szQuote);
01501     }
01502     else
01503     {
01504         /* do not quote integer record fields */
01505         val = msi_alloc(sz*sizeof(WCHAR));
01506         if (!val)
01507             return NULL;
01508 
01509         r = MSI_RecordGetStringW(rec, i, val, &sz);
01510     }
01511 
01512     if (r != ERROR_SUCCESS)
01513     {
01514         ERR("failed to get string!\n");
01515         msi_free(val);
01516         return NULL;
01517     }
01518 
01519     return val;
01520 }
01521 
01522 static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view,
01523                                     LPWSTR table, MSIRECORD *rec)
01524 {
01525     LPWSTR query = NULL, clause = NULL, val;
01526     LPCWSTR setptr, key;
01527     DWORD size, oldsize;
01528     MSIRECORD *keys;
01529     UINT r, i, count;
01530 
01531     static const WCHAR keyset[] = {
01532         '`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0};
01533     static const WCHAR lastkeyset[] = {
01534         '`','%','s','`',' ','=',' ','%','s',' ',0};
01535     static const WCHAR fmt[] = {'S','E','L','E','C','T',' ','*',' ',
01536         'F','R','O','M',' ','`','%','s','`',' ',
01537         'W','H','E','R','E',' ','%','s',0};
01538 
01539     r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys);
01540     if (r != ERROR_SUCCESS)
01541         return NULL;
01542 
01543     clause = msi_alloc_zero(sizeof(WCHAR));
01544     if (!clause)
01545         goto done;
01546 
01547     size = 1;
01548     count = MSI_RecordGetFieldCount(keys);
01549     for (i = 1; i <= count; i++)
01550     {
01551         key = MSI_RecordGetString(keys, i);
01552         val = get_key_value(view, key, rec);
01553 
01554         if (i == count)
01555             setptr = lastkeyset;
01556         else
01557             setptr = keyset;
01558 
01559         oldsize = size;
01560         size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4;
01561         clause = msi_realloc(clause, size * sizeof (WCHAR));
01562         if (!clause)
01563         {
01564             msi_free(val);
01565             goto done;
01566         }
01567 
01568         sprintfW(clause + oldsize - 1, setptr, key, val);
01569         msi_free(val);
01570     }
01571 
01572     size = lstrlenW(fmt) + lstrlenW(table) + lstrlenW(clause) + 1;
01573     query = msi_alloc(size * sizeof(WCHAR));
01574     if (!query)
01575         goto done;
01576 
01577     sprintfW(query, fmt, table, clause);
01578 
01579 done:
01580     msi_free(clause);
01581     msiobj_release(&keys->hdr);
01582     return query;
01583 }
01584 
01585 static UINT merge_diff_row(MSIRECORD *rec, LPVOID param)
01586 {
01587     MERGEDATA *data = param;
01588     MERGETABLE *table = data->curtable;
01589     MERGEROW *mergerow;
01590     MSIQUERY *dbview = NULL;
01591     MSIRECORD *row = NULL;
01592     LPWSTR query = NULL;
01593     UINT r = ERROR_SUCCESS;
01594 
01595     if (TABLE_Exists(data->db, table->name))
01596     {
01597         query = create_diff_row_query(data->merge, data->curview, table->name, rec);
01598         if (!query)
01599             return ERROR_OUTOFMEMORY;
01600 
01601         r = MSI_DatabaseOpenViewW(data->db, query, &dbview);
01602         if (r != ERROR_SUCCESS)
01603             goto done;
01604 
01605         r = MSI_ViewExecute(dbview, NULL);
01606         if (r != ERROR_SUCCESS)
01607             goto done;
01608 
01609         r = MSI_ViewFetch(dbview, &row);
01610         if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row))
01611         {
01612             table->numconflicts++;
01613             goto done;
01614         }
01615         else if (r != ERROR_NO_MORE_ITEMS)
01616             goto done;
01617 
01618         r = ERROR_SUCCESS;
01619     }
01620 
01621     mergerow = msi_alloc(sizeof(MERGEROW));
01622     if (!mergerow)
01623     {
01624         r = ERROR_OUTOFMEMORY;
01625         goto done;
01626     }
01627 
01628     mergerow->data = MSI_CloneRecord(rec);
01629     if (!mergerow->data)
01630     {
01631         r = ERROR_OUTOFMEMORY;
01632         msi_free(mergerow);
01633         goto done;
01634     }
01635 
01636     list_add_tail(&table->rows, &mergerow->entry);
01637 
01638 done:
01639     msi_free(query);
01640     msiobj_release(&row->hdr);
01641     msiobj_release(&dbview->hdr);
01642     return r;
01643 }
01644 
01645 static UINT msi_get_table_labels(MSIDATABASE *db, LPCWSTR table, LPWSTR **labels, DWORD *numlabels)
01646 {
01647     UINT r, i, count;
01648     MSIRECORD *prec = NULL;
01649 
01650     r = MSI_DatabaseGetPrimaryKeys(db, table, &prec);
01651     if (r != ERROR_SUCCESS)
01652         return r;
01653 
01654     count = MSI_RecordGetFieldCount(prec);
01655     *numlabels = count + 1;
01656     *labels = msi_alloc((*numlabels)*sizeof(LPWSTR));
01657     if (!*labels)
01658     {
01659         r = ERROR_OUTOFMEMORY;
01660         goto end;
01661     }
01662 
01663     (*labels)[0] = strdupW(table);
01664     for (i=1; i<=count; i++ )
01665     {
01666         (*labels)[i] = strdupW(MSI_RecordGetString(prec, i));
01667     }
01668 
01669 end:
01670     msiobj_release( &prec->hdr );
01671     return r;
01672 }
01673 
01674 static UINT msi_get_query_columns(MSIQUERY *query, LPWSTR **columns, DWORD *numcolumns)
01675 {
01676     UINT r, i, count;
01677     MSIRECORD *prec = NULL;
01678 
01679     r = MSI_ViewGetColumnInfo(query, MSICOLINFO_NAMES, &prec);
01680     if (r != ERROR_SUCCESS)
01681         return r;
01682 
01683     count = MSI_RecordGetFieldCount(prec);
01684     *columns = msi_alloc(count*sizeof(LPWSTR));
01685     if (!*columns)
01686     {
01687         r = ERROR_OUTOFMEMORY;
01688         goto end;
01689     }
01690 
01691     for (i=1; i<=count; i++ )
01692     {
01693         (*columns)[i-1] = strdupW(MSI_RecordGetString(prec, i));
01694     }
01695 
01696     *numcolumns = count;
01697 
01698 end:
01699     msiobj_release( &prec->hdr );
01700     return r;
01701 }
01702 
01703 static UINT msi_get_query_types(MSIQUERY *query, LPWSTR **types, DWORD *numtypes)
01704 {
01705     UINT r, i, count;
01706     MSIRECORD *prec = NULL;
01707 
01708     r = MSI_ViewGetColumnInfo(query, MSICOLINFO_TYPES, &prec);
01709     if (r != ERROR_SUCCESS)
01710         return r;
01711 
01712     count = MSI_RecordGetFieldCount(prec);
01713     *types = msi_alloc(count*sizeof(LPWSTR));
01714     if (!*types)
01715     {
01716         r = ERROR_OUTOFMEMORY;
01717         goto end;
01718     }
01719 
01720     *numtypes = count;
01721     for (i=1; i<=count; i++ )
01722     {
01723         (*types)[i-1] = strdupW(MSI_RecordGetString(prec, i));
01724     }
01725 
01726 end:
01727     msiobj_release( &prec->hdr );
01728     return r;
01729 }
01730 
01731 static void merge_free_rows(MERGETABLE *table)
01732 {
01733     struct list *item, *cursor;
01734 
01735     LIST_FOR_EACH_SAFE(item, cursor, &table->rows)
01736     {
01737         MERGEROW *row = LIST_ENTRY(item, MERGEROW, entry);
01738 
01739         list_remove(&row->entry);
01740         msiobj_release(&row->data->hdr);
01741         msi_free(row);
01742     }
01743 }
01744 
01745 static void free_merge_table(MERGETABLE *table)
01746 {
01747     UINT i;
01748 
01749     if (table->labels != NULL)
01750     {
01751         for (i = 0; i < table->numlabels; i++)
01752             msi_free(table->labels[i]);
01753 
01754         msi_free(table->labels);
01755     }
01756 
01757     if (table->columns != NULL)
01758     {
01759         for (i = 0; i < table->numcolumns; i++)
01760             msi_free(table->columns[i]);
01761 
01762         msi_free(table->columns);
01763     }
01764 
01765     if (table->types != NULL)
01766     {
01767         for (i = 0; i < table->numtypes; i++)
01768             msi_free(table->types[i]);
01769 
01770         msi_free(table->types);
01771     }
01772 
01773     msi_free(table->name);
01774     merge_free_rows(table);
01775 
01776     msi_free(table);
01777 }
01778 
01779 static UINT msi_get_merge_table (MSIDATABASE *db, LPCWSTR name, MERGETABLE **ptable)
01780 {
01781     UINT r;
01782     MERGETABLE *table;
01783     MSIQUERY *mergeview = NULL;
01784 
01785     static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
01786         'F','R','O','M',' ','`','%','s','`',0};
01787 
01788     table = msi_alloc_zero(sizeof(MERGETABLE));
01789     if (!table)
01790     {
01791        *ptable = NULL;
01792        return ERROR_OUTOFMEMORY;
01793     }
01794 
01795     r = msi_get_table_labels(db, name, &table->labels, &table->numlabels);
01796     if (r != ERROR_SUCCESS)
01797         goto err;
01798 
01799     r = MSI_OpenQuery(db, &mergeview, query, name);
01800     if (r != ERROR_SUCCESS)
01801         goto err;
01802 
01803     r = msi_get_query_columns(mergeview, &table->columns, &table->numcolumns);
01804     if (r != ERROR_SUCCESS)
01805         goto err;
01806 
01807     r = msi_get_query_types(mergeview, &table->types, &table->numtypes);
01808     if (r != ERROR_SUCCESS)
01809         goto err;
01810 
01811     list_init(&table->rows);
01812 
01813     table->name = strdupW(name);
01814     table->numconflicts = 0;
01815 
01816     msiobj_release(&mergeview->hdr);
01817     *ptable = table;
01818     return ERROR_SUCCESS;
01819 
01820 err:
01821     msiobj_release(&mergeview->hdr);
01822     free_merge_table(table);
01823     *ptable = NULL;
01824     return r;
01825 }
01826 
01827 static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
01828 {
01829     MERGEDATA *data = param;
01830     MERGETABLE *table;
01831     MSIQUERY *dbview = NULL;
01832     MSIQUERY *mergeview = NULL;
01833     LPCWSTR name;
01834     UINT r;
01835 
01836     static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
01837         'F','R','O','M',' ','`','%','s','`',0};
01838 
01839     name = MSI_RecordGetString(rec, 1);
01840 
01841     r = MSI_OpenQuery(data->merge, &mergeview, query, name);
01842     if (r != ERROR_SUCCESS)
01843         goto done;
01844 
01845     if (TABLE_Exists(data->db, name))
01846     {
01847         r = MSI_OpenQuery(data->db, &dbview, query, name);
01848         if (r != ERROR_SUCCESS)
01849             goto done;
01850 
01851         r = merge_verify_colnames(dbview, mergeview);
01852         if (r != ERROR_SUCCESS)
01853             goto done;
01854 
01855         r = merge_verify_primary_keys(data->db, data->merge, name);
01856         if (r != ERROR_SUCCESS)
01857             goto done;
01858     }
01859 
01860     r = msi_get_merge_table(data->merge, name, &table);
01861     if (r != ERROR_SUCCESS)
01862         goto done;
01863 
01864     data->curtable = table;
01865     data->curview = mergeview;
01866     r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
01867     if (r != ERROR_SUCCESS)
01868     {
01869         free_merge_table(table);
01870         goto done;
01871     }
01872 
01873     list_add_tail(data->tabledata, &table->entry);
01874 
01875 done:
01876     msiobj_release(&dbview->hdr);
01877     msiobj_release(&mergeview->hdr);
01878     return r;
01879 }
01880 
01881 static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge,
01882                               struct list *tabledata)
01883 {
01884     static const WCHAR query[] = {
01885         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
01886         '`','_','T','a','b','l','e','s','`',0};
01887     MSIQUERY *view;
01888     MERGEDATA data;
01889     UINT r;
01890 
01891     r = MSI_DatabaseOpenViewW(merge, query, &view);
01892     if (r != ERROR_SUCCESS)
01893         return r;
01894 
01895     data.db = db;
01896     data.merge = merge;
01897     data.tabledata = tabledata;
01898     r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data);
01899     msiobj_release(&view->hdr);
01900     return r;
01901 }
01902 
01903 static UINT merge_table(MSIDATABASE *db, MERGETABLE *table)
01904 {
01905     UINT r;
01906     MERGEROW *row;
01907     MSIVIEW *tv;
01908 
01909     if (!TABLE_Exists(db, table->name))
01910     {
01911         r = msi_add_table_to_db(db, table->columns, table->types,
01912                table->labels, table->numlabels, table->numcolumns);
01913         if (r != ERROR_SUCCESS)
01914            return ERROR_FUNCTION_FAILED;
01915     }
01916 
01917     LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry)
01918     {
01919         r = TABLE_CreateView(db, table->name, &tv);
01920         if (r != ERROR_SUCCESS)
01921             return r;
01922 
01923         r = tv->ops->insert_row(tv, row->data, -1, FALSE);
01924         tv->ops->delete(tv);
01925 
01926         if (r != ERROR_SUCCESS)
01927             return r;
01928     }
01929 
01930     return ERROR_SUCCESS;
01931 }
01932 
01933 static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error,
01934                                 LPWSTR table, DWORD numconflicts)
01935 {
01936     UINT r;
01937     MSIQUERY *view;
01938 
01939     static const WCHAR create[] = {
01940         'C','R','E','A','T','E',' ','T','A','B','L','E',' ',
01941         '`','%','s','`',' ','(','`','T','a','b','l','e','`',' ',
01942         'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ',
01943         'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e',
01944         'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ',
01945         'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ',
01946         'K','E','Y',' ','`','T','a','b','l','e','`',')',0};
01947     static const WCHAR insert[] = {
01948         'I','N','S','E','R','T',' ','I','N','T','O',' ',
01949         '`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ',
01950         '`','N','u','m','R','o','w','M','e','r','g','e',
01951         'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S',
01952         ' ','(','\'','%','s','\'',',',' ','%','d',')',0};
01953 
01954     if (!TABLE_Exists(db, error))
01955     {
01956         r = MSI_OpenQuery(db, &view, create, error);
01957         if (r != ERROR_SUCCESS)
01958             return r;
01959 
01960         r = MSI_ViewExecute(view, NULL);
01961         msiobj_release(&view->hdr);
01962         if (r != ERROR_SUCCESS)
01963             return r;
01964     }
01965 
01966     r = MSI_OpenQuery(db, &view, insert, error, table, numconflicts);
01967     if (r != ERROR_SUCCESS)
01968         return r;
01969 
01970     r = MSI_ViewExecute(view, NULL);
01971     msiobj_release(&view->hdr);
01972     return r;
01973 }
01974 
01975 UINT WINAPI MsiDatabaseMergeW(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
01976                               LPCWSTR szTableName)
01977 {
01978     struct list tabledata = LIST_INIT(tabledata);
01979     struct list *item, *cursor;
01980     MSIDATABASE *db, *merge;
01981     MERGETABLE *table;
01982     BOOL conflicts;
01983     UINT r;
01984 
01985     TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
01986           debugstr_w(szTableName));
01987 
01988     if (szTableName && !*szTableName)
01989         return ERROR_INVALID_TABLE;
01990 
01991     db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE);
01992     merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE);
01993     if (!db || !merge)
01994     {
01995         r = ERROR_INVALID_HANDLE;
01996         goto done;
01997     }
01998 
01999     r = gather_merge_data(db, merge, &tabledata);
02000     if (r != ERROR_SUCCESS)
02001         goto done;
02002 
02003     conflicts = FALSE;
02004     LIST_FOR_EACH_ENTRY(table, &tabledata, MERGETABLE, entry)
02005     {
02006         if (table->numconflicts)
02007         {
02008             conflicts = TRUE;
02009 
02010             r = update_merge_errors(db, szTableName, table->name,
02011                                     table->numconflicts);
02012             if (r != ERROR_SUCCESS)
02013                 break;
02014         }
02015         else
02016         {
02017             r = merge_table(db, table);
02018             if (r != ERROR_SUCCESS)
02019                 break;
02020         }
02021     }
02022 
02023     LIST_FOR_EACH_SAFE(item, cursor, &tabledata)
02024     {
02025         MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry);
02026         list_remove(&table->entry);
02027         free_merge_table(table);
02028     }
02029 
02030     if (conflicts)
02031         r = ERROR_FUNCTION_FAILED;
02032 
02033 done:
02034     msiobj_release(&db->hdr);
02035     msiobj_release(&merge->hdr);
02036     return r;
02037 }
02038 
02039 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
02040 {
02041     MSIDBSTATE ret = MSIDBSTATE_READ;
02042     MSIDATABASE *db;
02043 
02044     TRACE("%d\n", handle);
02045 
02046     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
02047     if( !db )
02048     {
02049         IWineMsiRemoteDatabase *remote_database;
02050 
02051         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
02052         if ( !remote_database )
02053             return MSIDBSTATE_ERROR;
02054 
02055         IWineMsiRemoteDatabase_Release( remote_database );
02056         WARN("MsiGetDatabaseState not allowed during a custom action!\n");
02057 
02058         return MSIDBSTATE_READ;
02059     }
02060 
02061     if (db->mode != MSIDBOPEN_READONLY )
02062         ret = MSIDBSTATE_WRITE;
02063     msiobj_release( &db->hdr );
02064 
02065     return ret;
02066 }
02067 
02068 typedef struct _msi_remote_database_impl {
02069     IWineMsiRemoteDatabase IWineMsiRemoteDatabase_iface;
02070     MSIHANDLE database;
02071     LONG refs;
02072 } msi_remote_database_impl;
02073 
02074 static inline msi_remote_database_impl *impl_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase *iface )
02075 {
02076     return CONTAINING_RECORD(iface, msi_remote_database_impl, IWineMsiRemoteDatabase_iface);
02077 }
02078 
02079 static HRESULT WINAPI mrd_QueryInterface( IWineMsiRemoteDatabase *iface,
02080                                           REFIID riid,LPVOID *ppobj)
02081 {
02082     if( IsEqualCLSID( riid, &IID_IUnknown ) ||
02083         IsEqualCLSID( riid, &IID_IWineMsiRemoteDatabase ) )
02084     {
02085         IUnknown_AddRef( iface );
02086         *ppobj = iface;
02087         return S_OK;
02088     }
02089 
02090     return E_NOINTERFACE;
02091 }
02092 
02093 static ULONG WINAPI mrd_AddRef( IWineMsiRemoteDatabase *iface )
02094 {
02095     msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface );
02096 
02097     return InterlockedIncrement( &This->refs );
02098 }
02099 
02100 static ULONG WINAPI mrd_Release( IWineMsiRemoteDatabase *iface )
02101 {
02102     msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface );
02103     ULONG r;
02104 
02105     r = InterlockedDecrement( &This->refs );
02106     if (r == 0)
02107     {
02108         MsiCloseHandle( This->database );
02109         msi_free( This );
02110     }
02111     return r;
02112 }
02113 
02114 static HRESULT WINAPI mrd_IsTablePersistent( IWineMsiRemoteDatabase *iface,
02115                                              LPCWSTR table, MSICONDITION *persistent )
02116 {
02117     msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface );
02118     *persistent = MsiDatabaseIsTablePersistentW(This->database, table);
02119     return S_OK;
02120 }
02121 
02122 static HRESULT WINAPI mrd_GetPrimaryKeys( IWineMsiRemoteDatabase *iface,
02123                                           LPCWSTR table, MSIHANDLE *keys )
02124 {
02125     msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface );
02126     UINT r = MsiDatabaseGetPrimaryKeysW(This->database, table, keys);
02127     return HRESULT_FROM_WIN32(r);
02128 }
02129 
02130 static HRESULT WINAPI mrd_GetSummaryInformation( IWineMsiRemoteDatabase *iface,
02131                                                 UINT updatecount, MSIHANDLE *suminfo )
02132 {
02133     msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface );
02134     UINT r = MsiGetSummaryInformationW(This->database, NULL, updatecount, suminfo);
02135     return HRESULT_FROM_WIN32(r);
02136 }
02137 
02138 static HRESULT WINAPI mrd_OpenView( IWineMsiRemoteDatabase *iface,
02139                                     LPCWSTR query, MSIHANDLE *view )
02140 {
02141     msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface );
02142     UINT r = MsiDatabaseOpenViewW(This->database, query, view);
02143     return HRESULT_FROM_WIN32(r);
02144 }
02145 
02146 static HRESULT WINAPI mrd_SetMsiHandle( IWineMsiRemoteDatabase *iface, MSIHANDLE handle )
02147 {
02148     msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface );
02149     This->database = handle;
02150     return S_OK;
02151 }
02152 
02153 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl =
02154 {
02155     mrd_QueryInterface,
02156     mrd_AddRef,
02157     mrd_Release,
02158     mrd_IsTablePersistent,
02159     mrd_GetPrimaryKeys,
02160     mrd_GetSummaryInformation,
02161     mrd_OpenView,
02162     mrd_SetMsiHandle,
02163 };
02164 
02165 HRESULT create_msi_remote_database( IUnknown *pOuter, LPVOID *ppObj )
02166 {
02167     msi_remote_database_impl *This;
02168 
02169     This = msi_alloc( sizeof *This );
02170     if (!This)
02171         return E_OUTOFMEMORY;
02172 
02173     This->IWineMsiRemoteDatabase_iface.lpVtbl = &msi_remote_database_vtbl;
02174     This->database = 0;
02175     This->refs = 1;
02176 
02177     *ppObj = This;
02178 
02179     return S_OK;
02180 }

Generated on Fri May 25 2012 04:16:59 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.