From 02fb530401132cac21406cbf81842d602c9e9baa Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Tue, 24 May 2011 10:49:37 +0200 Subject: [PATCH] msi: Move patch related functions to a separate file. --- 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 0f076b0de47..3954e9eeb99 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 f0c05d66703..61c07833146 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 26c2c4f5601..acef756958f 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 00000000000..00281a8d198 --- /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 +#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; +}