2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
5 * Copyright 2011 Hans Leidekker for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "wine/debug.h"
30 #include "wine/unicode.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
35 static BOOL
match_language( MSIPACKAGE
*package
, LANGID langid
)
39 if (!package
->num_langids
|| !langid
) return TRUE
;
40 for (i
= 0; i
< package
->num_langids
; i
++)
42 if (package
->langids
[i
] == langid
) return TRUE
;
47 static UINT
check_transform_applicable( MSIPACKAGE
*package
, IStorage
*transform
)
49 MSISUMMARYINFO
*si
= MSI_GetSummaryInformationW( transform
, 0 );
50 UINT valid_flags
= 0, wanted_flags
= 0;
52 if (si
) wanted_flags
= msi_suminfo_get_int32( si
, PID_CHARCOUNT
);
53 TRACE("validation flags %x\n", wanted_flags
);
55 if (wanted_flags
& ~(MSITRANSFORM_VALIDATE_PRODUCT
|MSITRANSFORM_VALIDATE_LANGUAGE
))
56 FIXME("unsupported validation flags %x\n", wanted_flags
);
58 if (wanted_flags
& MSITRANSFORM_VALIDATE_PRODUCT
)
60 WCHAR
*package_product
= msi_dup_property( package
->db
, szProductCode
);
61 WCHAR
*transform_product
= msi_get_suminfo_product( transform
);
63 TRACE("package = %s transform = %s\n", debugstr_w(package_product
), debugstr_w(transform_product
));
65 if (!transform_product
|| strstrW( transform_product
, package_product
))
67 valid_flags
|= MSITRANSFORM_VALIDATE_PRODUCT
;
69 msi_free( transform_product
);
70 msi_free( package_product
);
72 if (wanted_flags
& MSITRANSFORM_VALIDATE_LANGUAGE
)
79 ERR("no summary information!\n");
82 if (!(template = msi_suminfo_dup_string( si
, PID_TEMPLATE
)))
84 ERR("no template property!\n");
87 TRACE("template: %s\n", debugstr_w(template));
88 if (!template[0] || ((p
= strchrW( template, ';' )) && match_language( package
, atoiW( p
+ 1 ) )))
90 valid_flags
|= MSITRANSFORM_VALIDATE_LANGUAGE
;
96 msiobj_release( &si
->hdr
);
97 if (valid_flags
& ~wanted_flags
) return ERROR_FUNCTION_FAILED
;
98 TRACE("applicable transform\n");
102 static UINT
apply_substorage_transform( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
, LPCWSTR name
)
104 UINT ret
= ERROR_FUNCTION_FAILED
;
105 IStorage
*stg
= NULL
;
108 TRACE("%p %s\n", package
, debugstr_w(name
));
112 ERR("expected a colon in %s\n", debugstr_w(name
));
113 return ERROR_FUNCTION_FAILED
;
115 r
= IStorage_OpenStorage( patch_db
->storage
, name
, NULL
, STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
118 ret
= check_transform_applicable( package
, stg
);
119 if (ret
== ERROR_SUCCESS
)
120 msi_table_apply_transform( package
->db
, stg
);
122 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name
));
123 IStorage_Release( stg
);
127 ERR("failed to open substorage %s\n", debugstr_w(name
));
129 return ERROR_SUCCESS
;
132 UINT
msi_check_patch_applicable( MSIPACKAGE
*package
, MSISUMMARYINFO
*si
)
134 LPWSTR guid_list
, *guids
, product_code
;
135 UINT i
, ret
= ERROR_FUNCTION_FAILED
;
137 product_code
= msi_dup_property( package
->db
, szProductCode
);
140 /* FIXME: the property ProductCode should be written into the DB somewhere */
141 ERR("no product code to check\n");
142 return ERROR_SUCCESS
;
144 guid_list
= msi_suminfo_dup_string( si
, PID_TEMPLATE
);
145 guids
= msi_split_string( guid_list
, ';' );
146 for (i
= 0; guids
[i
] && ret
!= ERROR_SUCCESS
; i
++)
148 if (!strcmpW( guids
[i
], product_code
)) ret
= ERROR_SUCCESS
;
151 msi_free( guid_list
);
152 msi_free( product_code
);
156 static UINT
msi_parse_patch_summary( MSISUMMARYINFO
*si
, MSIPATCHINFO
**patch
)
159 UINT r
= ERROR_SUCCESS
;
162 if (!(pi
= msi_alloc_zero( sizeof(MSIPATCHINFO
) )))
164 return ERROR_OUTOFMEMORY
;
166 if (!(pi
->patchcode
= msi_suminfo_dup_string( si
, PID_REVNUMBER
)))
169 return ERROR_OUTOFMEMORY
;
174 msi_free( pi
->patchcode
);
176 return ERROR_PATCH_PACKAGE_INVALID
;
178 if (!(p
= strchrW( p
+ 1, '}' )))
180 msi_free( pi
->patchcode
);
182 return ERROR_PATCH_PACKAGE_INVALID
;
186 FIXME("patch obsoletes %s\n", debugstr_w(p
+ 1));
189 TRACE("patch code %s\n", debugstr_w(pi
->patchcode
));
190 if (!(pi
->products
= msi_suminfo_dup_string( si
, PID_TEMPLATE
)))
192 msi_free( pi
->patchcode
);
194 return ERROR_OUTOFMEMORY
;
196 if (!(pi
->transforms
= msi_suminfo_dup_string( si
, PID_LASTAUTHOR
)))
198 msi_free( pi
->patchcode
);
199 msi_free( pi
->products
);
201 return ERROR_OUTOFMEMORY
;
207 static UINT
patch_set_media_source_prop( MSIPACKAGE
*package
)
209 static const WCHAR query
[] = {
210 'S','E','L','E','C','T',' ','`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
211 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ',
212 'I','S',' ','N','O','T',' ','N','U','L','L',0};
215 const WCHAR
*property
;
219 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
220 if (r
!= ERROR_SUCCESS
)
223 r
= MSI_ViewExecute( view
, 0 );
224 if (r
!= ERROR_SUCCESS
)
227 if (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
229 property
= MSI_RecordGetString( rec
, 1 );
230 patch
= msi_dup_property( package
->db
, szPatch
);
231 msi_set_property( package
->db
, property
, patch
, -1 );
233 msiobj_release( &rec
->hdr
);
237 msiobj_release( &view
->hdr
);
248 struct patch_offset_list
251 UINT count
, min
, max
;
252 UINT offset_to_apply
;
255 static struct patch_offset_list
*patch_offset_list_create( void )
257 struct patch_offset_list
*pos
= msi_alloc( sizeof(struct patch_offset_list
) );
258 list_init( &pos
->files
);
259 pos
->count
= pos
->max
= 0;
264 static void patch_offset_list_free( struct patch_offset_list
*pos
)
266 struct patch_offset
*po
, *po2
;
268 LIST_FOR_EACH_ENTRY_SAFE( po
, po2
, &pos
->files
, struct patch_offset
, entry
)
270 msi_free( po
->name
);
276 static void patch_offset_get_patches( MSIDATABASE
*db
, UINT last_sequence
, struct patch_offset_list
*pos
)
278 static const WCHAR query
[] = {
279 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
280 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
281 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
286 r
= MSI_DatabaseOpenViewW( db
, query
, &view
);
287 if (r
!= ERROR_SUCCESS
)
290 rec
= MSI_CreateRecord( 1 );
291 MSI_RecordSetInteger( rec
, 1, last_sequence
);
293 r
= MSI_ViewExecute( view
, rec
);
294 msiobj_release( &rec
->hdr
);
295 if (r
!= ERROR_SUCCESS
)
298 while (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
300 UINT sequence
= MSI_RecordGetInteger( rec
, 2 );
302 /* FIXME: we only use the max/min sequence numbers for now */
303 pos
->min
= min( pos
->min
, sequence
);
304 pos
->max
= max( pos
->max
, sequence
);
306 msiobj_release( &rec
->hdr
);
308 msiobj_release( &view
->hdr
);
311 static void patch_offset_get_files( MSIDATABASE
*db
, UINT last_sequence
, struct patch_offset_list
*pos
)
313 static const WCHAR query
[] = {
314 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
315 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
316 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
321 r
= MSI_DatabaseOpenViewW( db
, query
, &view
);
322 if (r
!= ERROR_SUCCESS
)
325 rec
= MSI_CreateRecord( 1 );
326 MSI_RecordSetInteger( rec
, 1, last_sequence
);
328 r
= MSI_ViewExecute( view
, rec
);
329 msiobj_release( &rec
->hdr
);
330 if (r
!= ERROR_SUCCESS
)
333 while (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
335 UINT attributes
= MSI_RecordGetInteger( rec
, 7 );
336 if (attributes
& msidbFileAttributesPatchAdded
)
338 struct patch_offset
*po
= msi_alloc( sizeof(struct patch_offset
) );
340 po
->name
= msi_dup_record_field( rec
, 1 );
341 po
->sequence
= MSI_RecordGetInteger( rec
, 8 );
342 pos
->min
= min( pos
->min
, po
->sequence
);
343 pos
->max
= max( pos
->max
, po
->sequence
);
344 list_add_tail( &pos
->files
, &po
->entry
);
347 msiobj_release( &rec
->hdr
);
349 msiobj_release( &view
->hdr
);
352 static UINT
patch_offset_modify_db( MSIDATABASE
*db
, struct patch_offset_list
*pos
)
354 static const WCHAR query
[] = {
355 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
356 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','>','=',' ','?',' ',
357 'A','N','D',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
358 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
359 struct patch_offset
*po
;
364 r
= MSI_DatabaseOpenViewW( db
, query
, &view
);
365 if (r
!= ERROR_SUCCESS
)
366 return ERROR_SUCCESS
;
368 rec
= MSI_CreateRecord( 2 );
369 MSI_RecordSetInteger( rec
, 1, pos
->min
);
370 MSI_RecordSetInteger( rec
, 2, pos
->max
);
372 r
= MSI_ViewExecute( view
, rec
);
373 msiobj_release( &rec
->hdr
);
374 if (r
!= ERROR_SUCCESS
)
377 LIST_FOR_EACH_ENTRY( po
, &pos
->files
, struct patch_offset
, entry
)
380 while ((r_fetch
= MSI_ViewFetch( view
, &rec
)) == ERROR_SUCCESS
)
382 const WCHAR
*file
= MSI_RecordGetString( rec
, 1 );
385 if (!strcmpiW( file
, po
->name
))
388 seq
= MSI_RecordGetInteger( rec
, 8 );
389 MSI_RecordSetInteger( rec
, 8, seq
+ pos
->offset_to_apply
);
390 r
= MSI_ViewModify( view
, MSIMODIFY_UPDATE
, rec
);
391 if (r
!= ERROR_SUCCESS
)
392 ERR("Failed to update offset for file %s\n", debugstr_w(file
));
393 msiobj_release( &rec
->hdr
);
396 msiobj_release( &rec
->hdr
);
398 if (r_fetch
!= ERROR_SUCCESS
) break;
402 msiobj_release( &view
->hdr
);
403 return ERROR_SUCCESS
;
406 static const WCHAR patch_media_query
[] = {
407 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
408 'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
409 'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
410 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
423 static UINT
add_patch_media( MSIPACKAGE
*package
, IStorage
*patch
)
425 static const WCHAR delete_query
[] = {
426 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
427 'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0};
428 static const WCHAR insert_query
[] = {
429 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ',
430 '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',',
431 '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',',
432 '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ',
433 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
437 struct list media_list
;
438 struct patch_media
*media
, *next
;
440 r
= MSI_DatabaseOpenViewW( package
->db
, patch_media_query
, &view
);
441 if (r
!= ERROR_SUCCESS
) return r
;
443 r
= MSI_ViewExecute( view
, 0 );
444 if (r
!= ERROR_SUCCESS
)
446 msiobj_release( &view
->hdr
);
447 TRACE("query failed %u\n", r
);
450 list_init( &media_list
);
451 while (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
453 disk_id
= MSI_RecordGetInteger( rec
, 1 );
454 TRACE("disk_id %u\n", disk_id
);
455 if (disk_id
>= MSI_INITIAL_MEDIA_TRANSFORM_DISKID
)
457 msiobj_release( &rec
->hdr
);
460 if (!(media
= msi_alloc( sizeof( *media
)))) goto done
;
461 media
->disk_id
= disk_id
;
462 media
->last_sequence
= MSI_RecordGetInteger( rec
, 2 );
463 media
->prompt
= msi_dup_record_field( rec
, 3 );
464 media
->cabinet
= msi_dup_record_field( rec
, 4 );
465 media
->volume
= msi_dup_record_field( rec
, 5 );
466 media
->source
= msi_dup_record_field( rec
, 6 );
468 list_add_tail( &media_list
, &media
->entry
);
469 msiobj_release( &rec
->hdr
);
471 LIST_FOR_EACH_ENTRY( media
, &media_list
, struct patch_media
, entry
)
473 MSIQUERY
*delete_view
, *insert_view
;
475 r
= MSI_DatabaseOpenViewW( package
->db
, delete_query
, &delete_view
);
476 if (r
!= ERROR_SUCCESS
) goto done
;
478 rec
= MSI_CreateRecord( 1 );
479 MSI_RecordSetInteger( rec
, 1, media
->disk_id
);
481 r
= MSI_ViewExecute( delete_view
, rec
);
482 msiobj_release( &delete_view
->hdr
);
483 msiobj_release( &rec
->hdr
);
484 if (r
!= ERROR_SUCCESS
) goto done
;
486 r
= MSI_DatabaseOpenViewW( package
->db
, insert_query
, &insert_view
);
487 if (r
!= ERROR_SUCCESS
) goto done
;
489 disk_id
= package
->db
->media_transform_disk_id
;
490 TRACE("disk id %u\n", disk_id
);
491 TRACE("last sequence %u\n", media
->last_sequence
);
492 TRACE("prompt %s\n", debugstr_w(media
->prompt
));
493 TRACE("cabinet %s\n", debugstr_w(media
->cabinet
));
494 TRACE("volume %s\n", debugstr_w(media
->volume
));
495 TRACE("source %s\n", debugstr_w(media
->source
));
497 rec
= MSI_CreateRecord( 6 );
498 MSI_RecordSetInteger( rec
, 1, disk_id
);
499 MSI_RecordSetInteger( rec
, 2, media
->last_sequence
);
500 MSI_RecordSetStringW( rec
, 3, media
->prompt
);
501 MSI_RecordSetStringW( rec
, 4, media
->cabinet
);
502 MSI_RecordSetStringW( rec
, 5, media
->volume
);
503 MSI_RecordSetStringW( rec
, 6, media
->source
);
505 r
= MSI_ViewExecute( insert_view
, rec
);
506 msiobj_release( &insert_view
->hdr
);
507 msiobj_release( &rec
->hdr
);
508 if (r
!= ERROR_SUCCESS
) goto done
;
510 r
= msi_add_cabinet_stream( package
, disk_id
, patch
, media
->cabinet
);
511 if (r
!= ERROR_SUCCESS
) WARN("failed to add cabinet stream %u\n", r
);
512 package
->db
->media_transform_disk_id
++;
516 msiobj_release( &view
->hdr
);
517 LIST_FOR_EACH_ENTRY_SAFE( media
, next
, &media_list
, struct patch_media
, entry
)
519 list_remove( &media
->entry
);
520 msi_free( media
->prompt
);
521 msi_free( media
->cabinet
);
522 msi_free( media
->volume
);
523 msi_free( media
->source
);
529 static UINT
set_patch_offsets( MSIDATABASE
*db
)
535 r
= MSI_DatabaseOpenViewW( db
, patch_media_query
, &view
);
536 if (r
!= ERROR_SUCCESS
)
539 r
= MSI_ViewExecute( view
, 0 );
540 if (r
!= ERROR_SUCCESS
)
543 while (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
545 UINT last_sequence
= MSI_RecordGetInteger( rec
, 2 );
546 struct patch_offset_list
*pos
;
548 /* FIXME: set/check Source field instead? */
549 if (last_sequence
>= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET
)
551 msiobj_release( &rec
->hdr
);
554 pos
= patch_offset_list_create();
555 patch_offset_get_files( db
, last_sequence
, pos
);
556 patch_offset_get_patches( db
, last_sequence
, pos
);
558 UINT offset
= db
->media_transform_offset
- pos
->min
;
559 last_sequence
= offset
+ pos
->max
;
561 /* FIXME: this is for the patch table, which is not yet properly transformed */
562 last_sequence
+= pos
->min
;
563 pos
->offset_to_apply
= offset
;
565 patch_offset_modify_db( db
, pos
);
567 MSI_RecordSetInteger( rec
, 2, last_sequence
);
568 r
= MSI_ViewModify( view
, MSIMODIFY_UPDATE
, rec
);
569 if (r
!= ERROR_SUCCESS
)
570 ERR("Failed to update Media table entry, expect breakage (%u)\n", r
);
571 db
->media_transform_offset
= last_sequence
+ 1;
573 patch_offset_list_free( pos
);
574 msiobj_release( &rec
->hdr
);
578 msiobj_release( &view
->hdr
);
582 static UINT
msi_apply_patch_db( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
, MSIPATCHINFO
*patch
)
584 UINT i
, r
= ERROR_SUCCESS
;
587 /* apply substorage transforms */
588 substorage
= msi_split_string( patch
->transforms
, ';' );
589 for (i
= 0; substorage
&& substorage
[i
] && r
== ERROR_SUCCESS
; i
++)
591 r
= apply_substorage_transform( package
, patch_db
, substorage
[i
] );
592 if (r
== ERROR_SUCCESS
)
594 add_patch_media( package
, patch_db
->storage
);
595 set_patch_offsets( package
->db
);
598 msi_free( substorage
);
599 if (r
!= ERROR_SUCCESS
)
602 patch_set_media_source_prop( package
);
604 patch
->state
= MSIPATCHSTATE_APPLIED
;
605 list_add_tail( &package
->patches
, &patch
->entry
);
606 return ERROR_SUCCESS
;
609 void msi_free_patchinfo( MSIPATCHINFO
*patch
)
611 msi_free( patch
->patchcode
);
612 msi_free( patch
->products
);
613 msi_free( patch
->transforms
);
614 msi_free( patch
->filename
);
615 msi_free( patch
->localfile
);
619 static UINT
msi_apply_patch_package( MSIPACKAGE
*package
, const WCHAR
*file
)
621 static const WCHAR dotmsp
[] = {'.','m','s','p',0};
622 MSIDATABASE
*patch_db
= NULL
;
623 WCHAR localfile
[MAX_PATH
];
625 MSIPATCHINFO
*patch
= NULL
;
626 UINT r
= ERROR_SUCCESS
;
628 TRACE("%p %s\n", package
, debugstr_w(file
));
630 r
= MSI_OpenDatabaseW( file
, MSIDBOPEN_READONLY
+ MSIDBOPEN_PATCHFILE
, &patch_db
);
631 if (r
!= ERROR_SUCCESS
)
633 ERR("failed to open patch collection %s\n", debugstr_w( file
) );
636 if (!(si
= MSI_GetSummaryInformationW( patch_db
->storage
, 0 )))
638 msiobj_release( &patch_db
->hdr
);
639 return ERROR_FUNCTION_FAILED
;
641 r
= msi_check_patch_applicable( package
, si
);
642 if (r
!= ERROR_SUCCESS
)
644 TRACE("patch not applicable\n");
648 r
= msi_parse_patch_summary( si
, &patch
);
649 if ( r
!= ERROR_SUCCESS
)
652 r
= msi_create_empty_local_file( localfile
, dotmsp
);
653 if ( r
!= ERROR_SUCCESS
)
656 r
= ERROR_OUTOFMEMORY
;
657 if (!(patch
->filename
= strdupW( file
))) goto done
;
658 if (!(patch
->localfile
= strdupW( localfile
))) goto done
;
660 r
= msi_apply_patch_db( package
, patch_db
, patch
);
661 if (r
!= ERROR_SUCCESS
) WARN("patch failed to apply %u\n", r
);
664 msiobj_release( &si
->hdr
);
665 msiobj_release( &patch_db
->hdr
);
666 if (patch
&& r
!= ERROR_SUCCESS
)
668 DeleteFileW( patch
->localfile
);
669 msi_free_patchinfo( patch
);
674 /* get the PATCH property, and apply all the patches it specifies */
675 UINT
msi_apply_patches( MSIPACKAGE
*package
)
677 LPWSTR patch_list
, *patches
;
678 UINT i
, r
= ERROR_SUCCESS
;
680 patch_list
= msi_dup_property( package
->db
, szPatch
);
682 TRACE("patches to be applied: %s\n", debugstr_w(patch_list
));
684 patches
= msi_split_string( patch_list
, ';' );
685 for (i
= 0; patches
&& patches
[i
] && r
== ERROR_SUCCESS
; i
++)
686 r
= msi_apply_patch_package( package
, patches
[i
] );
689 msi_free( patch_list
);
693 UINT
msi_apply_transforms( MSIPACKAGE
*package
)
695 static const WCHAR szTransforms
[] = {'T','R','A','N','S','F','O','R','M','S',0};
696 LPWSTR xform_list
, *xforms
;
697 UINT i
, r
= ERROR_SUCCESS
;
699 xform_list
= msi_dup_property( package
->db
, szTransforms
);
700 xforms
= msi_split_string( xform_list
, ';' );
702 for (i
= 0; xforms
&& xforms
[i
] && r
== ERROR_SUCCESS
; i
++)
704 if (xforms
[i
][0] == ':')
705 r
= apply_substorage_transform( package
, package
->db
, xforms
[i
] );
710 if (!PathIsRelativeW( xforms
[i
] )) transform
= xforms
[i
];
713 WCHAR
*p
= strrchrW( package
->PackagePath
, '\\' );
714 DWORD len
= p
- package
->PackagePath
+ 1;
716 if (!(transform
= msi_alloc( (len
+ strlenW( xforms
[i
] ) + 1) * sizeof(WCHAR
)) ))
719 msi_free( xform_list
);
720 return ERROR_OUTOFMEMORY
;
722 memcpy( transform
, package
->PackagePath
, len
* sizeof(WCHAR
) );
723 memcpy( transform
+ len
, xforms
[i
], (strlenW( xforms
[i
] ) + 1) * sizeof(WCHAR
) );
725 r
= MSI_DatabaseApplyTransformW( package
->db
, transform
, 0 );
726 if (transform
!= xforms
[i
]) msi_free( transform
);
730 msi_free( xform_list
);
734 UINT
msi_apply_registered_patch( MSIPACKAGE
*package
, LPCWSTR patch_code
)
738 WCHAR patch_file
[MAX_PATH
];
739 MSIDATABASE
*patch_db
;
740 MSIPATCHINFO
*patch_info
;
743 len
= sizeof(patch_file
) / sizeof(WCHAR
);
744 r
= MsiGetPatchInfoExW( patch_code
, package
->ProductCode
, NULL
, package
->Context
,
745 INSTALLPROPERTY_LOCALPACKAGEW
, patch_file
, &len
);
746 if (r
!= ERROR_SUCCESS
)
748 ERR("failed to get patch filename %u\n", r
);
751 r
= MSI_OpenDatabaseW( patch_file
, MSIDBOPEN_READONLY
+ MSIDBOPEN_PATCHFILE
, &patch_db
);
752 if (r
!= ERROR_SUCCESS
)
754 ERR("failed to open patch database %s\n", debugstr_w( patch_file
));
757 si
= MSI_GetSummaryInformationW( patch_db
->storage
, 0 );
760 msiobj_release( &patch_db
->hdr
);
761 return ERROR_FUNCTION_FAILED
;
763 r
= msi_parse_patch_summary( si
, &patch_info
);
764 msiobj_release( &si
->hdr
);
765 if (r
!= ERROR_SUCCESS
)
767 ERR("failed to parse patch summary %u\n", r
);
768 msiobj_release( &patch_db
->hdr
);
771 patch_info
->localfile
= strdupW( patch_file
);
772 if (!patch_info
->localfile
)
774 msiobj_release( &patch_db
->hdr
);
775 msi_free_patchinfo( patch_info
);
776 return ERROR_OUTOFMEMORY
;
778 r
= msi_apply_patch_db( package
, patch_db
, patch_info
);
779 msiobj_release( &patch_db
->hdr
);
780 if (r
!= ERROR_SUCCESS
)
782 ERR("failed to apply patch %u\n", r
);
783 msi_free_patchinfo( patch_info
);