[01/10] msi: Move patch related functions to a separate file.
Hans Leidekker
hans at codeweavers.com
Tue May 24 03:49:37 CDT 2011
---
dlls/msi/Makefile.in | 1 +
dlls/msi/action.c | 751 --------------------------------------------------
dlls/msi/msipriv.h | 2 +
dlls/msi/patch.c | 719 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 722 insertions(+), 751 deletions(-)
create mode 100644 dlls/msi/patch.c
diff --git a/dlls/msi/Makefile.in b/dlls/msi/Makefile.in
index 0f076b0..3954e9e 100644
--- a/dlls/msi/Makefile.in
+++ b/dlls/msi/Makefile.in
@@ -30,6 +30,7 @@ C_SRCS = \
msi_main.c \
msiquery.c \
package.c \
+ patch.c \
preview.c \
record.c \
registry.c \
diff --git a/dlls/msi/action.c b/dlls/msi/action.c
index f0c05d6..61c0783 100644
--- a/dlls/msi/action.c
+++ b/dlls/msi/action.c
@@ -403,751 +403,6 @@ WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
return ret;
}
-static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
-{
- static const WCHAR szSystemLanguageID[] =
- { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
-
- LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
- UINT ret = ERROR_FUNCTION_FAILED;
-
- prod_code = msi_dup_property( package->db, szProductCode );
- patch_product = msi_get_suminfo_product( patch );
-
- TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
-
- if ( strstrW( patch_product, prod_code ) )
- {
- MSISUMMARYINFO *si;
- const WCHAR *p;
-
- si = MSI_GetSummaryInformationW( patch, 0 );
- if (!si)
- {
- ERR("no summary information!\n");
- goto end;
- }
-
- template = msi_suminfo_dup_string( si, PID_TEMPLATE );
- if (!template)
- {
- ERR("no template property!\n");
- msiobj_release( &si->hdr );
- goto end;
- }
-
- if (!template[0])
- {
- ret = ERROR_SUCCESS;
- msiobj_release( &si->hdr );
- goto end;
- }
-
- langid = msi_dup_property( package->db, szSystemLanguageID );
- if (!langid)
- {
- msiobj_release( &si->hdr );
- goto end;
- }
-
- p = strchrW( template, ';' );
- if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
- {
- TRACE("applicable transform\n");
- ret = ERROR_SUCCESS;
- }
-
- /* FIXME: check platform */
-
- msiobj_release( &si->hdr );
- }
-
-end:
- msi_free( patch_product );
- msi_free( prod_code );
- msi_free( template );
- msi_free( langid );
-
- return ret;
-}
-
-static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
- MSIDATABASE *patch_db, LPCWSTR name )
-{
- UINT ret = ERROR_FUNCTION_FAILED;
- IStorage *stg = NULL;
- HRESULT r;
-
- TRACE("%p %s\n", package, debugstr_w(name) );
-
- if (*name++ != ':')
- {
- ERR("expected a colon in %s\n", debugstr_w(name));
- return ERROR_FUNCTION_FAILED;
- }
-
- r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
- if (SUCCEEDED(r))
- {
- ret = msi_check_transform_applicable( package, stg );
- if (ret == ERROR_SUCCESS)
- msi_table_apply_transform( package->db, stg );
- else
- TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
- IStorage_Release( stg );
- }
- else
- ERR("failed to open substorage %s\n", debugstr_w(name));
-
- return ERROR_SUCCESS;
-}
-
-UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
-{
- LPWSTR guid_list, *guids, product_code;
- UINT i, ret = ERROR_FUNCTION_FAILED;
-
- product_code = msi_dup_property( package->db, szProductCode );
- if (!product_code)
- {
- /* FIXME: the property ProductCode should be written into the DB somewhere */
- ERR("no product code to check\n");
- return ERROR_SUCCESS;
- }
-
- guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
- guids = msi_split_string( guid_list, ';' );
- for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
- {
- if (!strcmpW( guids[i], product_code ))
- ret = ERROR_SUCCESS;
- }
- msi_free( guids );
- msi_free( guid_list );
- msi_free( product_code );
-
- return ret;
-}
-
-static UINT msi_set_media_source_prop(MSIPACKAGE *package)
-{
- MSIQUERY *view;
- MSIRECORD *rec = NULL;
- LPWSTR patch;
- LPCWSTR prop;
- UINT r;
-
- static const WCHAR query[] = {'S','E','L','E','C','T',' ',
- '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
- '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
- '`','S','o','u','r','c','e','`',' ','I','S',' ',
- 'N','O','T',' ','N','U','L','L',0};
-
- r = MSI_DatabaseOpenViewW(package->db, query, &view);
- if (r != ERROR_SUCCESS)
- return r;
-
- r = MSI_ViewExecute(view, 0);
- if (r != ERROR_SUCCESS)
- goto done;
-
- if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
- {
- prop = MSI_RecordGetString(rec, 1);
- patch = msi_dup_property(package->db, szPatch);
- msi_set_property(package->db, prop, patch);
- msi_free(patch);
- }
-
-done:
- if (rec) msiobj_release(&rec->hdr);
- msiobj_release(&view->hdr);
-
- return r;
-}
-
-UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
-{
- MSIPATCHINFO *pi;
- UINT r = ERROR_SUCCESS;
- WCHAR *p;
-
- pi = msi_alloc_zero( sizeof(MSIPATCHINFO) );
- if (!pi)
- return ERROR_OUTOFMEMORY;
-
- pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER );
- if (!pi->patchcode)
- {
- msi_free( pi );
- return ERROR_OUTOFMEMORY;
- }
-
- p = pi->patchcode;
- if (*p != '{')
- {
- msi_free( pi->patchcode );
- msi_free( pi );
- return ERROR_PATCH_PACKAGE_INVALID;
- }
-
- p = strchrW( p + 1, '}' );
- if (!p)
- {
- msi_free( pi->patchcode );
- msi_free( pi );
- return ERROR_PATCH_PACKAGE_INVALID;
- }
-
- if (p[1])
- {
- FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
- p[1] = 0;
- }
-
- TRACE("patch code %s\n", debugstr_w(pi->patchcode));
-
- pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
- if (!pi->transforms)
- {
- msi_free( pi->patchcode );
- msi_free( pi );
- return ERROR_OUTOFMEMORY;
- }
-
- *patch = pi;
- return r;
-}
-
-struct msi_patch_offset
-{
- struct list entry;
- LPWSTR Name;
- UINT Sequence;
-};
-
-struct msi_patch_offset_list
-{
- struct list files;
- UINT count, min, max;
- UINT offset_to_apply;
-};
-
-static struct msi_patch_offset_list *msi_patch_offset_list_create(void)
-{
- struct msi_patch_offset_list *pos = msi_alloc(sizeof(struct msi_patch_offset_list));
- list_init( &pos->files );
- pos->count = pos->max = 0;
- pos->min = 999999;
-
- return pos;
-}
-
-static void msi_patch_offset_list_free(struct msi_patch_offset_list *pos)
-{
- struct msi_patch_offset *po, *po2;
-
- LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct msi_patch_offset, entry )
- {
- msi_free( po->Name );
- msi_free( po );
- }
-
- msi_free( pos );
-}
-
-static void msi_patch_offset_get_patches(MSIDATABASE *db, UINT last_sequence, struct msi_patch_offset_list *pos)
-{
- MSIQUERY *view;
- MSIRECORD *rec;
- UINT r;
- static const WCHAR query_patch[] = {
- 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
- 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
- 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
-
- r = MSI_DatabaseOpenViewW( db, query_patch, &view );
- if (r != ERROR_SUCCESS)
- return;
-
- rec = MSI_CreateRecord( 1 );
- MSI_RecordSetInteger(rec, 1, last_sequence);
-
- r = MSI_ViewExecute( view, rec );
- msiobj_release( &rec->hdr );
- if (r != ERROR_SUCCESS)
- return;
-
- while (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
- {
- UINT sequence = MSI_RecordGetInteger( rec, 2 );
-
- /* FIXME:
- * We only use the max/min sequence numbers for now.
- */
-
- pos->min = min(pos->min, sequence);
- pos->max = max(pos->max, sequence);
- pos->count++;
-
- msiobj_release( &rec->hdr );
- }
-
- msiobj_release( &view->hdr );
-}
-
-static void msi_patch_offset_get_files(MSIDATABASE *db, UINT last_sequence, struct msi_patch_offset_list *pos)
-{
- MSIQUERY *view;
- MSIRECORD *rec;
- UINT r;
- static const WCHAR query_files[] = {
- 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
- 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
- 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
-
- r = MSI_DatabaseOpenViewW( db, query_files, &view );
- if (r != ERROR_SUCCESS)
- return;
-
- rec = MSI_CreateRecord( 1 );
- MSI_RecordSetInteger(rec, 1, last_sequence);
-
- r = MSI_ViewExecute( view, rec );
- msiobj_release( &rec->hdr );
- if (r != ERROR_SUCCESS)
- return;
-
- while (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
- {
- UINT attributes = MSI_RecordGetInteger( rec, 7 );
- if (attributes & msidbFileAttributesPatchAdded)
- {
- struct msi_patch_offset *po = msi_alloc(sizeof(struct msi_patch_offset));
-
- po->Name = msi_dup_record_field( rec, 1 );
- po->Sequence = MSI_RecordGetInteger( rec, 8 );
-
- pos->min = min(pos->min, po->Sequence);
- pos->max = max(pos->max, po->Sequence);
-
- list_add_tail( &pos->files, &po->entry );
- pos->count++;
- }
- msiobj_release( &rec->hdr );
- }
-
- msiobj_release( &view->hdr );
-}
-
-static UINT msi_patch_offset_modify_db(MSIDATABASE *db, struct msi_patch_offset_list *pos)
-{
- static const WCHAR query_files[] =
- {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
- 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','>','=',' ','?',' ',
- 'A','N','D',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
- 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
- struct msi_patch_offset *po;
- MSIQUERY *view;
- MSIRECORD *rec;
- UINT r;
-
- r = MSI_DatabaseOpenViewW( db, query_files, &view );
- if (r != ERROR_SUCCESS)
- return ERROR_SUCCESS;
-
- rec = MSI_CreateRecord( 2 );
- MSI_RecordSetInteger( rec, 1, pos->min );
- MSI_RecordSetInteger( rec, 2, pos->max );
-
- r = MSI_ViewExecute( view, rec );
- msiobj_release( &rec->hdr );
- if (r != ERROR_SUCCESS)
- goto done;
-
- LIST_FOR_EACH_ENTRY( po, &pos->files, struct msi_patch_offset, entry )
- {
- UINT r_fetch;
- while ( (r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS )
- {
- LPCWSTR file = MSI_RecordGetString( rec, 1 );
- UINT seq;
-
- if (!strcmpiW(file, po->Name))
- {
- /* Update record */
- seq = MSI_RecordGetInteger( rec, 8 );
- MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply );
- r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
- if (r != ERROR_SUCCESS)
- ERR("Failed to update offset for file %s.\n", debugstr_w(file));
-
- msiobj_release( &rec->hdr );
- break;
- }
-
- msiobj_release( &rec->hdr );
- }
-
- if (r_fetch != ERROR_SUCCESS)
- break;
- }
-
-done:
- msiobj_release( &view->hdr );
-
- return ERROR_SUCCESS;
-}
-
-static const WCHAR patch_media_query[] = {
- 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
- 'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
- 'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
- 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
-
-struct patch_media
-{
- struct list entry;
- UINT disk_id;
- UINT last_sequence;
- WCHAR *prompt;
- WCHAR *cabinet;
- WCHAR *volume;
- WCHAR *source;
-};
-
-static UINT msi_add_patch_media( MSIPACKAGE *package, IStorage *patch )
-{
- static const WCHAR delete_query[] = {
- 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
- 'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0};
- static const WCHAR insert_query[] = {
- 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ',
- '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',',
- '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',',
- '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ',
- 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
- MSIQUERY *view;
- MSIRECORD *rec = NULL;
- UINT r, disk_id;
- struct list media_list;
- struct patch_media *media, *next;
-
- r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view );
- if (r != ERROR_SUCCESS) return r;
-
- r = MSI_ViewExecute( view, 0 );
- if (r != ERROR_SUCCESS)
- {
- msiobj_release( &view->hdr );
- TRACE("query failed %u\n", r);
- return r;
- }
-
- list_init( &media_list );
- while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
- {
- disk_id = MSI_RecordGetInteger( rec, 1 );
- TRACE("disk_id %u\n", disk_id);
- if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
- {
- msiobj_release( &rec->hdr );
- continue;
- }
- if (!(media = msi_alloc( sizeof( *media )))) goto done;
- media->disk_id = disk_id;
- media->last_sequence = MSI_RecordGetInteger( rec, 2 );
- media->prompt = msi_dup_record_field( rec, 3 );
- media->cabinet = msi_dup_record_field( rec, 4 );
- media->volume = msi_dup_record_field( rec, 5 );
- media->source = msi_dup_record_field( rec, 6 );
-
- list_add_tail( &media_list, &media->entry );
- msiobj_release( &rec->hdr );
- }
- LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry )
- {
- MSIQUERY *delete_view, *insert_view;
-
- r = MSI_DatabaseOpenViewW( package->db, delete_query, &delete_view );
- if (r != ERROR_SUCCESS) goto done;
-
- rec = MSI_CreateRecord( 1 );
- MSI_RecordSetInteger( rec, 1, media->disk_id );
-
- r = MSI_ViewExecute( delete_view, rec );
- msiobj_release( &delete_view->hdr );
- msiobj_release( &rec->hdr );
- if (r != ERROR_SUCCESS) goto done;
-
- r = MSI_DatabaseOpenViewW( package->db, insert_query, &insert_view );
- if (r != ERROR_SUCCESS) goto done;
-
- disk_id = package->db->media_transform_disk_id;
- TRACE("disk id %u\n", disk_id);
- TRACE("last sequence %u\n", media->last_sequence);
- TRACE("prompt %s\n", debugstr_w(media->prompt));
- TRACE("cabinet %s\n", debugstr_w(media->cabinet));
- TRACE("volume %s\n", debugstr_w(media->volume));
- TRACE("source %s\n", debugstr_w(media->source));
-
- rec = MSI_CreateRecord( 6 );
- MSI_RecordSetInteger( rec, 1, disk_id );
- MSI_RecordSetInteger( rec, 2, media->last_sequence );
- MSI_RecordSetStringW( rec, 3, media->prompt );
- MSI_RecordSetStringW( rec, 4, media->cabinet );
- MSI_RecordSetStringW( rec, 5, media->volume );
- MSI_RecordSetStringW( rec, 6, media->source );
-
- r = MSI_ViewExecute( insert_view, rec );
- msiobj_release( &insert_view->hdr );
- msiobj_release( &rec->hdr );
- if (r != ERROR_SUCCESS) goto done;
-
- r = msi_add_cabinet_stream( package, disk_id, patch, media->cabinet );
- if (r != ERROR_SUCCESS) WARN("failed to add cabinet stream %u\n", r);
- package->db->media_transform_disk_id++;
- }
-
-done:
- msiobj_release( &view->hdr );
- LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry )
- {
- list_remove( &media->entry );
- msi_free( media->prompt );
- msi_free( media->cabinet );
- msi_free( media->volume );
- msi_free( media->source );
- msi_free( media );
- }
- return r;
-}
-
-static UINT msi_set_patch_offsets(MSIDATABASE *db)
-{
- MSIQUERY *view;
- MSIRECORD *rec = NULL;
- UINT r;
-
- r = MSI_DatabaseOpenViewW( db, patch_media_query, &view );
- if (r != ERROR_SUCCESS)
- return r;
-
- r = MSI_ViewExecute( view, 0 );
- if (r != ERROR_SUCCESS)
- goto done;
-
- while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
- {
- UINT last_sequence = MSI_RecordGetInteger( rec, 2 );
- struct msi_patch_offset_list *pos;
-
- /* FIXME: Set/Check Source field instead? */
- if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
- {
- msiobj_release( &rec->hdr );
- continue;
- }
-
- pos = msi_patch_offset_list_create();
-
- msi_patch_offset_get_files( db, last_sequence, pos );
- msi_patch_offset_get_patches( db, last_sequence, pos );
-
- if (pos->count)
- {
- UINT offset = db->media_transform_offset - pos->min;
- last_sequence = offset + pos->max;
-
- /* FIXME:
- * This is for the patch table, which is not yet properly transformed.
- */
- last_sequence += pos->min;
-
- pos->offset_to_apply = offset;
- msi_patch_offset_modify_db( db, pos );
-
- MSI_RecordSetInteger( rec, 2, last_sequence );
- r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
- if (r != ERROR_SUCCESS)
- ERR("Failed to update Media table entry, expect breakage (%u).\n", r);
-
- db->media_transform_offset = last_sequence + 1;
- }
-
- msi_patch_offset_list_free( pos );
- msiobj_release( &rec->hdr );
- }
-
-done:
- msiobj_release( &view->hdr );
- return r;
-}
-
-UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
-{
- UINT i, r = ERROR_SUCCESS;
- WCHAR **substorage;
-
- /* apply substorage transforms */
- substorage = msi_split_string( patch->transforms, ';' );
- for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
- {
- r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
- if (r == ERROR_SUCCESS)
- {
- msi_add_patch_media( package, patch_db->storage );
- msi_set_patch_offsets( package->db );
- }
- }
-
- msi_free( substorage );
- if (r != ERROR_SUCCESS)
- return r;
-
- msi_set_media_source_prop( package );
-
- patch->state = MSIPATCHSTATE_APPLIED;
- list_add_tail( &package->patches, &patch->entry );
- return ERROR_SUCCESS;
-}
-
-static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
-{
- static const WCHAR dotmsp[] = {'.','m','s','p',0};
- MSIDATABASE *patch_db = NULL;
- WCHAR localfile[MAX_PATH];
- MSISUMMARYINFO *si;
- MSIPATCHINFO *patch = NULL;
- UINT r = ERROR_SUCCESS;
-
- TRACE("%p %s\n", package, debugstr_w( file ) );
-
- r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
- if ( r != ERROR_SUCCESS )
- {
- ERR("failed to open patch collection %s\n", debugstr_w( file ) );
- return r;
- }
-
- si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
- if (!si)
- {
- msiobj_release( &patch_db->hdr );
- return ERROR_FUNCTION_FAILED;
- }
-
- r = msi_check_patch_applicable( package, si );
- if (r != ERROR_SUCCESS)
- {
- TRACE("patch not applicable\n");
- r = ERROR_SUCCESS;
- goto done;
- }
-
- r = msi_parse_patch_summary( si, &patch );
- if ( r != ERROR_SUCCESS )
- goto done;
-
- r = msi_get_local_package_name( localfile, dotmsp );
- if ( r != ERROR_SUCCESS )
- goto done;
-
- TRACE("copying to local package %s\n", debugstr_w(localfile));
-
- if (!CopyFileW( file, localfile, FALSE ))
- {
- ERR("Unable to copy package (%s -> %s) (error %u)\n",
- debugstr_w(file), debugstr_w(localfile), GetLastError());
- r = GetLastError();
- goto done;
- }
- patch->localfile = strdupW( localfile );
-
- r = msi_apply_patch_db( package, patch_db, patch );
- if ( r != ERROR_SUCCESS )
- WARN("patch failed to apply %u\n", r);
-
-done:
- msiobj_release( &si->hdr );
- msiobj_release( &patch_db->hdr );
- if (patch && r != ERROR_SUCCESS)
- {
- if (patch->localfile)
- DeleteFileW( patch->localfile );
-
- msi_free( patch->patchcode );
- msi_free( patch->transforms );
- msi_free( patch->localfile );
- msi_free( patch );
- }
- return r;
-}
-
-/* get the PATCH property, and apply all the patches it specifies */
-static UINT msi_apply_patches( MSIPACKAGE *package )
-{
- LPWSTR patch_list, *patches;
- UINT i, r = ERROR_SUCCESS;
-
- patch_list = msi_dup_property( package->db, szPatch );
-
- TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
-
- patches = msi_split_string( patch_list, ';' );
- for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
- r = msi_apply_patch_package( package, patches[i] );
-
- msi_free( patches );
- msi_free( patch_list );
-
- return r;
-}
-
-static UINT msi_apply_transforms( MSIPACKAGE *package )
-{
- static const WCHAR szTransforms[] = {
- 'T','R','A','N','S','F','O','R','M','S',0 };
- LPWSTR xform_list, *xforms;
- UINT i, r = ERROR_SUCCESS;
-
- xform_list = msi_dup_property( package->db, szTransforms );
- xforms = msi_split_string( xform_list, ';' );
-
- for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
- {
- if (xforms[i][0] == ':')
- r = msi_apply_substorage_transform( package, package->db, xforms[i] );
- else
- {
- WCHAR *transform;
-
- if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
- else
- {
- WCHAR *p = strrchrW( package->PackagePath, '\\' );
- DWORD len = p - package->PackagePath + 1;
-
- if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
- {
- msi_free( xforms );
- msi_free( xform_list );
- return ERROR_OUTOFMEMORY;
- }
- memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
- memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
- }
- r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
- if (transform != xforms[i]) msi_free( transform );
- }
- }
-
- msi_free( xforms );
- msi_free( xform_list );
-
- return r;
-}
-
static BOOL ui_sequence_exists( MSIPACKAGE *package )
{
MSIQUERY *view;
@@ -4916,12 +4171,6 @@ done:
return r;
}
-/*
- * 99% of the work done here is only done for
- * advertised installs. However this is where the
- * Icon table is processed and written out
- * so that is what I am going to do here.
- */
static UINT ACTION_PublishProduct(MSIPACKAGE *package)
{
UINT rc;
diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h
index 26c2c4f..acef756 100644
--- a/dlls/msi/msipriv.h
+++ b/dlls/msi/msipriv.h
@@ -755,11 +755,13 @@ extern UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg ) DECLSPEC
extern UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db,
LPCWSTR szTransformFile, int iErrorCond ) DECLSPEC_HIDDEN;
extern void append_storage_to_db( MSIDATABASE *db, IStorage *stg ) DECLSPEC_HIDDEN;
+extern UINT msi_apply_transforms( MSIPACKAGE *package ) DECLSPEC_HIDDEN;
/* patch functions */
extern UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si ) DECLSPEC_HIDDEN;
extern UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch ) DECLSPEC_HIDDEN;
extern UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch ) DECLSPEC_HIDDEN;
+extern UINT msi_apply_patches( MSIPACKAGE *package ) DECLSPEC_HIDDEN;
/* action internals */
extern UINT MSI_InstallPackage( MSIPACKAGE *, LPCWSTR, LPCWSTR ) DECLSPEC_HIDDEN;
diff --git a/dlls/msi/patch.c b/dlls/msi/patch.c
new file mode 100644
index 0000000..00281a8
--- /dev/null
+++ b/dlls/msi/patch.c
@@ -0,0 +1,719 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2004,2005 Aric Stewart for CodeWeavers
+ * Copyright 2011 Hans Leidekker for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+#define COBJMACROS
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "objbase.h"
+#include "shlwapi.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+#include "msipriv.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+static UINT check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
+{
+ static const WCHAR szSystemLanguageID[] = {
+ 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0};
+ LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
+ UINT ret = ERROR_FUNCTION_FAILED;
+
+ prod_code = msi_dup_property( package->db, szProductCode );
+ patch_product = msi_get_suminfo_product( patch );
+
+ TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
+
+ if (strstrW( patch_product, prod_code ))
+ {
+ MSISUMMARYINFO *si;
+ const WCHAR *p;
+
+ si = MSI_GetSummaryInformationW( patch, 0 );
+ if (!si)
+ {
+ ERR("no summary information!\n");
+ goto end;
+ }
+ template = msi_suminfo_dup_string( si, PID_TEMPLATE );
+ if (!template)
+ {
+ ERR("no template property!\n");
+ msiobj_release( &si->hdr );
+ goto end;
+ }
+ if (!template[0])
+ {
+ ret = ERROR_SUCCESS;
+ msiobj_release( &si->hdr );
+ goto end;
+ }
+ langid = msi_dup_property( package->db, szSystemLanguageID );
+ if (!langid)
+ {
+ msiobj_release( &si->hdr );
+ goto end;
+ }
+ p = strchrW( template, ';' );
+ if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
+ {
+ TRACE("applicable transform\n");
+ ret = ERROR_SUCCESS;
+ }
+ /* FIXME: check platform */
+ msiobj_release( &si->hdr );
+ }
+
+end:
+ msi_free( patch_product );
+ msi_free( prod_code );
+ msi_free( template );
+ msi_free( langid );
+ return ret;
+}
+
+static UINT apply_substorage_transform( MSIPACKAGE *package, MSIDATABASE *patch_db, LPCWSTR name )
+{
+ UINT ret = ERROR_FUNCTION_FAILED;
+ IStorage *stg = NULL;
+ HRESULT r;
+
+ TRACE("%p %s\n", package, debugstr_w(name));
+
+ if (*name++ != ':')
+ {
+ ERR("expected a colon in %s\n", debugstr_w(name));
+ return ERROR_FUNCTION_FAILED;
+ }
+ r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
+ if (SUCCEEDED(r))
+ {
+ ret = check_transform_applicable( package, stg );
+ if (ret == ERROR_SUCCESS)
+ msi_table_apply_transform( package->db, stg );
+ else
+ TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
+ IStorage_Release( stg );
+ }
+ else
+ {
+ ERR("failed to open substorage %s\n", debugstr_w(name));
+ }
+ return ERROR_SUCCESS;
+}
+
+UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
+{
+ LPWSTR guid_list, *guids, product_code;
+ UINT i, ret = ERROR_FUNCTION_FAILED;
+
+ product_code = msi_dup_property( package->db, szProductCode );
+ if (!product_code)
+ {
+ /* FIXME: the property ProductCode should be written into the DB somewhere */
+ ERR("no product code to check\n");
+ return ERROR_SUCCESS;
+ }
+ guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
+ guids = msi_split_string( guid_list, ';' );
+ for (i = 0; guids[i] && ret != ERROR_SUCCESS; i++)
+ {
+ if (!strcmpW( guids[i], product_code )) ret = ERROR_SUCCESS;
+ }
+ msi_free( guids );
+ msi_free( guid_list );
+ msi_free( product_code );
+ return ret;
+}
+
+UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
+{
+ MSIPATCHINFO *pi;
+ UINT r = ERROR_SUCCESS;
+ WCHAR *p;
+
+ if (!(pi = msi_alloc_zero( sizeof(MSIPATCHINFO) )))
+ {
+ return ERROR_OUTOFMEMORY;
+ }
+ if (!(pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER )))
+ {
+ msi_free( pi );
+ return ERROR_OUTOFMEMORY;
+ }
+ p = pi->patchcode;
+ if (*p != '{')
+ {
+ msi_free( pi->patchcode );
+ msi_free( pi );
+ return ERROR_PATCH_PACKAGE_INVALID;
+ }
+ if (!(p = strchrW( p + 1, '}' )))
+ {
+ msi_free( pi->patchcode );
+ msi_free( pi );
+ return ERROR_PATCH_PACKAGE_INVALID;
+ }
+ if (p[1])
+ {
+ FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
+ p[1] = 0;
+ }
+ TRACE("patch code %s\n", debugstr_w(pi->patchcode));
+
+ if (!(pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR )))
+ {
+ msi_free( pi->patchcode );
+ msi_free( pi );
+ return ERROR_OUTOFMEMORY;
+ }
+ *patch = pi;
+ return r;
+}
+
+static UINT patch_set_media_source_prop( MSIPACKAGE *package )
+{
+ static const WCHAR query[] = {
+ 'S','E','L','E','C','T',' ','`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
+ '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ',
+ 'I','S',' ','N','O','T',' ','N','U','L','L',0};
+ MSIQUERY *view;
+ MSIRECORD *rec;
+ const WCHAR *property;
+ WCHAR *patch;
+ UINT r;
+
+ r = MSI_DatabaseOpenViewW( package->db, query, &view );
+ if (r != ERROR_SUCCESS)
+ return r;
+
+ r = MSI_ViewExecute( view, 0 );
+ if (r != ERROR_SUCCESS)
+ goto done;
+
+ if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
+ {
+ property = MSI_RecordGetString( rec, 1 );
+ patch = msi_dup_property( package->db, szPatch );
+ msi_set_property( package->db, property, patch );
+ msi_free( patch );
+ msiobj_release( &rec->hdr );
+ }
+
+done:
+ msiobj_release( &view->hdr );
+ return r;
+}
+
+struct patch_offset
+{
+ struct list entry;
+ WCHAR *name;
+ UINT sequence;
+};
+
+struct patch_offset_list
+{
+ struct list files;
+ UINT count, min, max;
+ UINT offset_to_apply;
+};
+
+static struct patch_offset_list *patch_offset_list_create( void )
+{
+ struct patch_offset_list *pos = msi_alloc( sizeof(struct patch_offset_list) );
+ list_init( &pos->files );
+ pos->count = pos->max = 0;
+ pos->min = 999999;
+ return pos;
+}
+
+static void patch_offset_list_free( struct patch_offset_list *pos )
+{
+ struct patch_offset *po, *po2;
+
+ LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct patch_offset, entry )
+ {
+ msi_free( po->name );
+ msi_free( po );
+ }
+ msi_free( pos );
+}
+
+static void patch_offset_get_patches( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
+{
+ static const WCHAR query_patch[] = {
+ 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
+ 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
+ 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
+ MSIQUERY *view;
+ MSIRECORD *rec;
+ UINT r;
+
+ r = MSI_DatabaseOpenViewW( db, query_patch, &view );
+ if (r != ERROR_SUCCESS)
+ return;
+
+ rec = MSI_CreateRecord( 1 );
+ MSI_RecordSetInteger( rec, 1, last_sequence );
+
+ r = MSI_ViewExecute( view, rec );
+ msiobj_release( &rec->hdr );
+ if (r != ERROR_SUCCESS)
+ return;
+
+ while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
+ {
+ UINT sequence = MSI_RecordGetInteger( rec, 2 );
+
+ /* FIXME: we only use the max/min sequence numbers for now */
+ pos->min = min( pos->min, sequence );
+ pos->max = max( pos->max, sequence );
+ pos->count++;
+ msiobj_release( &rec->hdr );
+ }
+ msiobj_release( &view->hdr );
+}
+
+static void patch_offset_get_files( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
+{
+ static const WCHAR query_files[] = {
+ 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
+ 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
+ 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
+ MSIQUERY *view;
+ MSIRECORD *rec;
+ UINT r;
+
+ r = MSI_DatabaseOpenViewW( db, query_files, &view );
+ if (r != ERROR_SUCCESS)
+ return;
+
+ rec = MSI_CreateRecord( 1 );
+ MSI_RecordSetInteger( rec, 1, last_sequence );
+
+ r = MSI_ViewExecute( view, rec );
+ msiobj_release( &rec->hdr );
+ if (r != ERROR_SUCCESS)
+ return;
+
+ while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
+ {
+ UINT attributes = MSI_RecordGetInteger( rec, 7 );
+ if (attributes & msidbFileAttributesPatchAdded)
+ {
+ struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) );
+
+ po->name = msi_dup_record_field( rec, 1 );
+ po->sequence = MSI_RecordGetInteger( rec, 8 );
+ pos->min = min( pos->min, po->sequence );
+ pos->max = max( pos->max, po->sequence );
+ list_add_tail( &pos->files, &po->entry );
+ pos->count++;
+ }
+ msiobj_release( &rec->hdr );
+ }
+ msiobj_release( &view->hdr );
+}
+
+static UINT patch_offset_modify_db( MSIDATABASE *db, struct patch_offset_list *pos )
+{
+ static const WCHAR query_files[] = {
+ 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
+ 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','>','=',' ','?',' ',
+ 'A','N','D',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
+ 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
+ struct patch_offset *po;
+ MSIRECORD *rec;
+ MSIQUERY *view;
+ UINT r;
+
+ r = MSI_DatabaseOpenViewW( db, query_files, &view );
+ if (r != ERROR_SUCCESS)
+ return ERROR_SUCCESS;
+
+ rec = MSI_CreateRecord( 2 );
+ MSI_RecordSetInteger( rec, 1, pos->min );
+ MSI_RecordSetInteger( rec, 2, pos->max );
+
+ r = MSI_ViewExecute( view, rec );
+ msiobj_release( &rec->hdr );
+ if (r != ERROR_SUCCESS)
+ goto done;
+
+ LIST_FOR_EACH_ENTRY( po, &pos->files, struct patch_offset, entry )
+ {
+ UINT r_fetch;
+ while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
+ {
+ const WCHAR *file = MSI_RecordGetString( rec, 1 );
+ UINT seq;
+
+ if (!strcmpiW( file, po->name ))
+ {
+ /* update record */
+ seq = MSI_RecordGetInteger( rec, 8 );
+ MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply );
+ r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
+ if (r != ERROR_SUCCESS)
+ ERR("Failed to update offset for file %s\n", debugstr_w(file));
+ msiobj_release( &rec->hdr );
+ break;
+ }
+ msiobj_release( &rec->hdr );
+ }
+ if (r_fetch != ERROR_SUCCESS) break;
+ }
+
+done:
+ msiobj_release( &view->hdr );
+ return ERROR_SUCCESS;
+}
+
+static const WCHAR patch_media_query[] = {
+ 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
+ 'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
+ 'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
+ 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
+
+struct patch_media
+{
+ struct list entry;
+ UINT disk_id;
+ UINT last_sequence;
+ WCHAR *prompt;
+ WCHAR *cabinet;
+ WCHAR *volume;
+ WCHAR *source;
+};
+
+static UINT add_patch_media( MSIPACKAGE *package, IStorage *patch )
+{
+ static const WCHAR delete_query[] = {
+ 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
+ 'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0};
+ static const WCHAR insert_query[] = {
+ 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ',
+ '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',',
+ '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',',
+ '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ',
+ 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
+ MSIQUERY *view;
+ MSIRECORD *rec;
+ UINT r, disk_id;
+ struct list media_list;
+ struct patch_media *media, *next;
+
+ r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view );
+ if (r != ERROR_SUCCESS) return r;
+
+ r = MSI_ViewExecute( view, 0 );
+ if (r != ERROR_SUCCESS)
+ {
+ msiobj_release( &view->hdr );
+ TRACE("query failed %u\n", r);
+ return r;
+ }
+ list_init( &media_list );
+ while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
+ {
+ disk_id = MSI_RecordGetInteger( rec, 1 );
+ TRACE("disk_id %u\n", disk_id);
+ if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
+ {
+ msiobj_release( &rec->hdr );
+ continue;
+ }
+ if (!(media = msi_alloc( sizeof( *media )))) goto done;
+ media->disk_id = disk_id;
+ media->last_sequence = MSI_RecordGetInteger( rec, 2 );
+ media->prompt = msi_dup_record_field( rec, 3 );
+ media->cabinet = msi_dup_record_field( rec, 4 );
+ media->volume = msi_dup_record_field( rec, 5 );
+ media->source = msi_dup_record_field( rec, 6 );
+
+ list_add_tail( &media_list, &media->entry );
+ msiobj_release( &rec->hdr );
+ }
+ LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry )
+ {
+ MSIQUERY *delete_view, *insert_view;
+
+ r = MSI_DatabaseOpenViewW( package->db, delete_query, &delete_view );
+ if (r != ERROR_SUCCESS) goto done;
+
+ rec = MSI_CreateRecord( 1 );
+ MSI_RecordSetInteger( rec, 1, media->disk_id );
+
+ r = MSI_ViewExecute( delete_view, rec );
+ msiobj_release( &delete_view->hdr );
+ msiobj_release( &rec->hdr );
+ if (r != ERROR_SUCCESS) goto done;
+
+ r = MSI_DatabaseOpenViewW( package->db, insert_query, &insert_view );
+ if (r != ERROR_SUCCESS) goto done;
+
+ disk_id = package->db->media_transform_disk_id;
+ TRACE("disk id %u\n", disk_id);
+ TRACE("last sequence %u\n", media->last_sequence);
+ TRACE("prompt %s\n", debugstr_w(media->prompt));
+ TRACE("cabinet %s\n", debugstr_w(media->cabinet));
+ TRACE("volume %s\n", debugstr_w(media->volume));
+ TRACE("source %s\n", debugstr_w(media->source));
+
+ rec = MSI_CreateRecord( 6 );
+ MSI_RecordSetInteger( rec, 1, disk_id );
+ MSI_RecordSetInteger( rec, 2, media->last_sequence );
+ MSI_RecordSetStringW( rec, 3, media->prompt );
+ MSI_RecordSetStringW( rec, 4, media->cabinet );
+ MSI_RecordSetStringW( rec, 5, media->volume );
+ MSI_RecordSetStringW( rec, 6, media->source );
+
+ r = MSI_ViewExecute( insert_view, rec );
+ msiobj_release( &insert_view->hdr );
+ msiobj_release( &rec->hdr );
+ if (r != ERROR_SUCCESS) goto done;
+
+ r = msi_add_cabinet_stream( package, disk_id, patch, media->cabinet );
+ if (r != ERROR_SUCCESS) WARN("failed to add cabinet stream %u\n", r);
+ package->db->media_transform_disk_id++;
+ }
+
+done:
+ msiobj_release( &view->hdr );
+ LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry )
+ {
+ list_remove( &media->entry );
+ msi_free( media->prompt );
+ msi_free( media->cabinet );
+ msi_free( media->volume );
+ msi_free( media->source );
+ msi_free( media );
+ }
+ return r;
+}
+
+static UINT set_patch_offsets( MSIDATABASE *db )
+{
+ MSIQUERY *view;
+ MSIRECORD *rec;
+ UINT r;
+
+ r = MSI_DatabaseOpenViewW( db, patch_media_query, &view );
+ if (r != ERROR_SUCCESS)
+ return r;
+
+ r = MSI_ViewExecute( view, 0 );
+ if (r != ERROR_SUCCESS)
+ goto done;
+
+ while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
+ {
+ UINT last_sequence = MSI_RecordGetInteger( rec, 2 );
+ struct patch_offset_list *pos;
+
+ /* FIXME: set/check Source field instead? */
+ if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
+ {
+ msiobj_release( &rec->hdr );
+ continue;
+ }
+ pos = patch_offset_list_create();
+ patch_offset_get_files( db, last_sequence, pos );
+ patch_offset_get_patches( db, last_sequence, pos );
+ if (pos->count)
+ {
+ UINT offset = db->media_transform_offset - pos->min;
+ last_sequence = offset + pos->max;
+
+ /* FIXME: this is for the patch table, which is not yet properly transformed */
+ last_sequence += pos->min;
+ pos->offset_to_apply = offset;
+ patch_offset_modify_db( db, pos );
+
+ MSI_RecordSetInteger( rec, 2, last_sequence );
+ r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
+ if (r != ERROR_SUCCESS)
+ ERR("Failed to update Media table entry, expect breakage (%u)\n", r);
+ db->media_transform_offset = last_sequence + 1;
+ }
+ patch_offset_list_free( pos );
+ msiobj_release( &rec->hdr );
+ }
+
+done:
+ msiobj_release( &view->hdr );
+ return r;
+}
+
+UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
+{
+ UINT i, r = ERROR_SUCCESS;
+ WCHAR **substorage;
+
+ /* apply substorage transforms */
+ substorage = msi_split_string( patch->transforms, ';' );
+ for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
+ {
+ r = apply_substorage_transform( package, patch_db, substorage[i] );
+ if (r == ERROR_SUCCESS)
+ {
+ add_patch_media( package, patch_db->storage );
+ set_patch_offsets( package->db );
+ }
+ }
+ msi_free( substorage );
+ if (r != ERROR_SUCCESS)
+ return r;
+
+ patch_set_media_source_prop( package );
+
+ patch->state = MSIPATCHSTATE_APPLIED;
+ list_add_tail( &package->patches, &patch->entry );
+ return ERROR_SUCCESS;
+}
+
+static UINT msi_apply_patch_package( MSIPACKAGE *package, const WCHAR *file )
+{
+ static const WCHAR dotmsp[] = {'.','m','s','p',0};
+ MSIDATABASE *patch_db = NULL;
+ WCHAR localfile[MAX_PATH];
+ MSISUMMARYINFO *si;
+ MSIPATCHINFO *patch = NULL;
+ UINT r = ERROR_SUCCESS;
+
+ TRACE("%p %s\n", package, debugstr_w(file));
+
+ r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
+ if (r != ERROR_SUCCESS)
+ {
+ ERR("failed to open patch collection %s\n", debugstr_w( file ) );
+ return r;
+ }
+ if (!(si = MSI_GetSummaryInformationW( patch_db->storage, 0 )))
+ {
+ msiobj_release( &patch_db->hdr );
+ return ERROR_FUNCTION_FAILED;
+ }
+ r = msi_check_patch_applicable( package, si );
+ if (r != ERROR_SUCCESS)
+ {
+ TRACE("patch not applicable\n");
+ r = ERROR_SUCCESS;
+ goto done;
+ }
+ r = msi_parse_patch_summary( si, &patch );
+ if ( r != ERROR_SUCCESS )
+ goto done;
+
+ r = msi_get_local_package_name( localfile, dotmsp );
+ if ( r != ERROR_SUCCESS )
+ goto done;
+
+ TRACE("copying to local package %s\n", debugstr_w(localfile));
+
+ if (!CopyFileW( file, localfile, FALSE ))
+ {
+ ERR("Unable to copy package (%s -> %s) (error %u)\n",
+ debugstr_w(file), debugstr_w(localfile), GetLastError());
+ r = GetLastError();
+ goto done;
+ }
+ patch->localfile = strdupW( localfile );
+
+ r = msi_apply_patch_db( package, patch_db, patch );
+ if (r != ERROR_SUCCESS) WARN("patch failed to apply %u\n", r);
+
+done:
+ msiobj_release( &si->hdr );
+ msiobj_release( &patch_db->hdr );
+ if (patch && r != ERROR_SUCCESS)
+ {
+ if (patch->localfile) DeleteFileW( patch->localfile );
+ msi_free( patch->patchcode );
+ msi_free( patch->transforms );
+ msi_free( patch->localfile );
+ msi_free( patch );
+ }
+ return r;
+}
+
+/* get the PATCH property, and apply all the patches it specifies */
+UINT msi_apply_patches( MSIPACKAGE *package )
+{
+ LPWSTR patch_list, *patches;
+ UINT i, r = ERROR_SUCCESS;
+
+ patch_list = msi_dup_property( package->db, szPatch );
+
+ TRACE("patches to be applied: %s\n", debugstr_w(patch_list));
+
+ patches = msi_split_string( patch_list, ';' );
+ for (i = 0; patches && patches[i] && r == ERROR_SUCCESS; i++)
+ r = msi_apply_patch_package( package, patches[i] );
+
+ msi_free( patches );
+ msi_free( patch_list );
+ return r;
+}
+
+UINT msi_apply_transforms( MSIPACKAGE *package )
+{
+ static const WCHAR szTransforms[] = {'T','R','A','N','S','F','O','R','M','S',0};
+ LPWSTR xform_list, *xforms;
+ UINT i, r = ERROR_SUCCESS;
+
+ xform_list = msi_dup_property( package->db, szTransforms );
+ xforms = msi_split_string( xform_list, ';' );
+
+ for (i = 0; xforms && xforms[i] && r == ERROR_SUCCESS; i++)
+ {
+ if (xforms[i][0] == ':')
+ r = apply_substorage_transform( package, package->db, xforms[i] );
+ else
+ {
+ WCHAR *transform;
+
+ if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
+ else
+ {
+ WCHAR *p = strrchrW( package->PackagePath, '\\' );
+ DWORD len = p - package->PackagePath + 1;
+
+ if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
+ {
+ msi_free( xforms );
+ msi_free( xform_list );
+ return ERROR_OUTOFMEMORY;
+ }
+ memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
+ memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
+ }
+ r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
+ if (transform != xforms[i]) msi_free( transform );
+ }
+ }
+ msi_free( xforms );
+ msi_free( xform_list );
+ return r;
+}
--
1.7.4.1
More information about the wine-patches
mailing list