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
;
49 WCHAR
*product_code_from
;
50 WCHAR
*product_code_to
;
56 static void free_transform_desc( struct transform_desc
*desc
)
58 msi_free( desc
->product_code_from
);
59 msi_free( desc
->product_code_to
);
60 msi_free( desc
->version_from
);
61 msi_free( desc
->version_to
);
62 msi_free( desc
->upgrade_code
);
66 static struct transform_desc
*parse_transform_desc( const WCHAR
*str
)
68 struct transform_desc
*ret
;
69 const WCHAR
*p
= str
, *q
;
72 if (!(ret
= msi_alloc_zero( sizeof(*ret
) ))) return NULL
;
74 q
= strchrW( p
, '}' );
75 if (*p
!= '{' || !q
) goto error
;
78 if (!(ret
->product_code_from
= msi_alloc( (len
+ 1) * sizeof(WCHAR
) ))) goto error
;
79 memcpy( ret
->product_code_from
, p
, len
* sizeof(WCHAR
) );
80 ret
->product_code_from
[len
] = 0;
83 if (!(q
= strchrW( p
, ';' ))) goto error
;
85 if (!(ret
->version_from
= msi_alloc( (len
+ 1) * sizeof(WCHAR
) ))) goto error
;
86 memcpy( ret
->version_from
, p
, len
* sizeof(WCHAR
) );
87 ret
->version_from
[len
] = 0;
90 q
= strchrW( p
, '}' );
91 if (*p
!= '{' || !q
) goto error
;
94 if (!(ret
->product_code_to
= msi_alloc( (len
+ 1) * sizeof(WCHAR
) ))) goto error
;
95 memcpy( ret
->product_code_to
, p
, len
* sizeof(WCHAR
) );
96 ret
->product_code_to
[len
] = 0;
99 if (!(q
= strchrW( p
, ';' ))) goto error
;
101 if (!(ret
->version_to
= msi_alloc( (len
+ 1) * sizeof(WCHAR
) ))) goto error
;
102 memcpy( ret
->version_to
, p
, len
* sizeof(WCHAR
) );
103 ret
->version_to
[len
] = 0;
106 q
= strchrW( p
, '}' );
107 if (*p
!= '{' || !q
) goto error
;
110 if (!(ret
->upgrade_code
= msi_alloc( (len
+ 1) * sizeof(WCHAR
) ))) goto error
;
111 memcpy( ret
->upgrade_code
, p
, len
* sizeof(WCHAR
) );
112 ret
->upgrade_code
[len
] = 0;
117 free_transform_desc( ret
);
121 static UINT
check_transform_applicable( MSIPACKAGE
*package
, IStorage
*transform
)
123 static const UINT supported_flags
=
124 MSITRANSFORM_VALIDATE_PRODUCT
| MSITRANSFORM_VALIDATE_LANGUAGE
|
125 MSITRANSFORM_VALIDATE_PLATFORM
| MSITRANSFORM_VALIDATE_MAJORVERSION
|
126 MSITRANSFORM_VALIDATE_MINORVERSION
| MSITRANSFORM_VALIDATE_UPGRADECODE
;
127 MSISUMMARYINFO
*si
= MSI_GetSummaryInformationW( transform
, 0 );
128 UINT valid_flags
= 0, wanted_flags
= 0;
129 WCHAR
*template, *product
, *p
;
130 struct transform_desc
*desc
;
134 WARN("no summary information!\n");
135 return ERROR_FUNCTION_FAILED
;
137 wanted_flags
= msi_suminfo_get_int32( si
, PID_CHARCOUNT
);
138 wanted_flags
&= 0xffff; /* mask off error condition flags */
139 TRACE("validation flags 0x%04x\n", wanted_flags
);
141 if (wanted_flags
& ~supported_flags
)
143 FIXME("unsupported validation flags 0x%04x\n", wanted_flags
);
144 msiobj_release( &si
->hdr
);
145 return ERROR_FUNCTION_FAILED
;
147 if (!(template = msi_suminfo_dup_string( si
, PID_TEMPLATE
)))
149 WARN("no template property!\n");
150 msiobj_release( &si
->hdr
);
151 return ERROR_FUNCTION_FAILED
;
153 TRACE("template property: %s\n", debugstr_w(template));
154 if (!(product
= msi_get_suminfo_product( transform
)))
156 WARN("no product property!\n");
157 msi_free( template );
158 msiobj_release( &si
->hdr
);
159 return ERROR_FUNCTION_FAILED
;
161 TRACE("product property: %s\n", debugstr_w(product
));
162 if (!(desc
= parse_transform_desc( product
)))
164 msi_free( template );
165 msiobj_release( &si
->hdr
);
166 return ERROR_FUNCTION_FAILED
;
170 if (wanted_flags
& MSITRANSFORM_VALIDATE_LANGUAGE
)
172 if (!template[0] || ((p
= strchrW( template, ';' )) && match_language( package
, atoiW( p
+ 1 ) )))
174 valid_flags
|= MSITRANSFORM_VALIDATE_LANGUAGE
;
177 if (wanted_flags
& MSITRANSFORM_VALIDATE_PRODUCT
)
179 WCHAR
*product_code_installed
= msi_dup_property( package
->db
, szProductCode
);
181 if (!product_code_installed
)
183 msi_free( template );
184 free_transform_desc( desc
);
185 msiobj_release( &si
->hdr
);
186 return ERROR_INSTALL_PACKAGE_INVALID
;
188 if (!strcmpW( desc
->product_code_from
, product_code_installed
))
190 valid_flags
|= MSITRANSFORM_VALIDATE_PRODUCT
;
192 msi_free( product_code_installed
);
194 if (wanted_flags
& MSITRANSFORM_VALIDATE_PLATFORM
)
196 if ((p
= strchrW( template, ';' )))
199 if (package
->platform
== parse_platform( template ))
200 valid_flags
|= MSITRANSFORM_VALIDATE_PLATFORM
;
203 msi_free( template );
204 if (wanted_flags
& MSITRANSFORM_VALIDATE_MAJORVERSION
)
206 WCHAR
*product_version_installed
= msi_dup_property( package
->db
, szProductVersion
);
207 DWORD major_installed
, minor_installed
, major
, minor
;
209 if (!product_version_installed
)
211 free_transform_desc( desc
);
212 msiobj_release( &si
->hdr
);
213 return ERROR_INSTALL_PACKAGE_INVALID
;
215 msi_parse_version_string( product_version_installed
, &major_installed
, &minor_installed
);
216 msi_parse_version_string( desc
->version_from
, &major
, &minor
);
218 if (major_installed
== major
)
220 valid_flags
|= MSITRANSFORM_VALIDATE_MAJORVERSION
;
221 wanted_flags
&= ~MSITRANSFORM_VALIDATE_MINORVERSION
;
223 msi_free( product_version_installed
);
225 else if (wanted_flags
& MSITRANSFORM_VALIDATE_MINORVERSION
)
227 WCHAR
*product_version_installed
= msi_dup_property( package
->db
, szProductVersion
);
228 DWORD major_installed
, minor_installed
, major
, minor
;
230 if (!product_version_installed
)
232 free_transform_desc( desc
);
233 msiobj_release( &si
->hdr
);
234 return ERROR_INSTALL_PACKAGE_INVALID
;
236 msi_parse_version_string( product_version_installed
, &major_installed
, &minor_installed
);
237 msi_parse_version_string( desc
->version_from
, &major
, &minor
);
239 if (major_installed
== major
&& minor_installed
== minor
)
240 valid_flags
|= MSITRANSFORM_VALIDATE_MINORVERSION
;
241 msi_free( product_version_installed
);
243 if (wanted_flags
& MSITRANSFORM_VALIDATE_UPGRADECODE
)
245 WCHAR
*upgrade_code_installed
= msi_dup_property( package
->db
, szUpgradeCode
);
247 if (!upgrade_code_installed
)
249 free_transform_desc( desc
);
250 msiobj_release( &si
->hdr
);
251 return ERROR_INSTALL_PACKAGE_INVALID
;
253 if (!strcmpW( desc
->upgrade_code
, upgrade_code_installed
))
254 valid_flags
|= MSITRANSFORM_VALIDATE_UPGRADECODE
;
255 msi_free( upgrade_code_installed
);
258 free_transform_desc( desc
);
259 msiobj_release( &si
->hdr
);
260 if ((valid_flags
& wanted_flags
) != wanted_flags
) return ERROR_FUNCTION_FAILED
;
261 TRACE("applicable transform\n");
262 return ERROR_SUCCESS
;
265 static UINT
apply_substorage_transform( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
, LPCWSTR name
)
267 UINT ret
= ERROR_FUNCTION_FAILED
;
268 IStorage
*stg
= NULL
;
271 TRACE("%p %s\n", package
, debugstr_w(name
));
275 ERR("expected a colon in %s\n", debugstr_w(name
));
276 return ERROR_FUNCTION_FAILED
;
278 r
= IStorage_OpenStorage( patch_db
->storage
, name
, NULL
, STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
281 ret
= check_transform_applicable( package
, stg
);
282 if (ret
== ERROR_SUCCESS
)
283 msi_table_apply_transform( package
->db
, stg
);
285 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name
));
286 IStorage_Release( stg
);
290 ERR("failed to open substorage %s\n", debugstr_w(name
));
292 return ERROR_SUCCESS
;
295 UINT
msi_check_patch_applicable( MSIPACKAGE
*package
, MSISUMMARYINFO
*si
)
297 LPWSTR guid_list
, *guids
, product_code
;
298 UINT i
, ret
= ERROR_FUNCTION_FAILED
;
300 product_code
= msi_dup_property( package
->db
, szProductCode
);
303 /* FIXME: the property ProductCode should be written into the DB somewhere */
304 ERR("no product code to check\n");
305 return ERROR_SUCCESS
;
307 guid_list
= msi_suminfo_dup_string( si
, PID_TEMPLATE
);
308 guids
= msi_split_string( guid_list
, ';' );
309 for (i
= 0; guids
[i
] && ret
!= ERROR_SUCCESS
; i
++)
311 if (!strcmpW( guids
[i
], product_code
)) ret
= ERROR_SUCCESS
;
314 msi_free( guid_list
);
315 msi_free( product_code
);
319 static UINT
msi_parse_patch_summary( MSISUMMARYINFO
*si
, MSIPATCHINFO
**patch
)
322 UINT r
= ERROR_SUCCESS
;
325 if (!(pi
= msi_alloc_zero( sizeof(MSIPATCHINFO
) )))
327 return ERROR_OUTOFMEMORY
;
329 if (!(pi
->patchcode
= msi_suminfo_dup_string( si
, PID_REVNUMBER
)))
332 return ERROR_OUTOFMEMORY
;
337 msi_free( pi
->patchcode
);
339 return ERROR_PATCH_PACKAGE_INVALID
;
341 if (!(p
= strchrW( p
+ 1, '}' )))
343 msi_free( pi
->patchcode
);
345 return ERROR_PATCH_PACKAGE_INVALID
;
349 FIXME("patch obsoletes %s\n", debugstr_w(p
+ 1));
352 TRACE("patch code %s\n", debugstr_w(pi
->patchcode
));
353 if (!(pi
->products
= msi_suminfo_dup_string( si
, PID_TEMPLATE
)))
355 msi_free( pi
->patchcode
);
357 return ERROR_OUTOFMEMORY
;
359 if (!(pi
->transforms
= msi_suminfo_dup_string( si
, PID_LASTAUTHOR
)))
361 msi_free( pi
->patchcode
);
362 msi_free( pi
->products
);
364 return ERROR_OUTOFMEMORY
;
370 static UINT
patch_set_media_source_prop( MSIPACKAGE
*package
)
372 static const WCHAR query
[] = {
373 'S','E','L','E','C','T',' ','`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
374 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ',
375 'I','S',' ','N','O','T',' ','N','U','L','L',0};
378 const WCHAR
*property
;
382 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
383 if (r
!= ERROR_SUCCESS
)
386 r
= MSI_ViewExecute( view
, 0 );
387 if (r
!= ERROR_SUCCESS
)
390 if (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
392 property
= MSI_RecordGetString( rec
, 1 );
393 patch
= msi_dup_property( package
->db
, szPatch
);
394 msi_set_property( package
->db
, property
, patch
, -1 );
396 msiobj_release( &rec
->hdr
);
400 msiobj_release( &view
->hdr
);
411 struct patch_offset_list
414 UINT count
, min
, max
;
415 UINT offset_to_apply
;
418 static struct patch_offset_list
*patch_offset_list_create( void )
420 struct patch_offset_list
*pos
= msi_alloc( sizeof(struct patch_offset_list
) );
421 list_init( &pos
->files
);
422 pos
->count
= pos
->max
= 0;
427 static void patch_offset_list_free( struct patch_offset_list
*pos
)
429 struct patch_offset
*po
, *po2
;
431 LIST_FOR_EACH_ENTRY_SAFE( po
, po2
, &pos
->files
, struct patch_offset
, entry
)
433 msi_free( po
->name
);
439 static void patch_offset_get_patches( MSIDATABASE
*db
, UINT last_sequence
, struct patch_offset_list
*pos
)
441 static const WCHAR query
[] = {
442 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
443 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
444 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
449 r
= MSI_DatabaseOpenViewW( db
, query
, &view
);
450 if (r
!= ERROR_SUCCESS
)
453 rec
= MSI_CreateRecord( 1 );
454 MSI_RecordSetInteger( rec
, 1, last_sequence
);
456 r
= MSI_ViewExecute( view
, rec
);
457 msiobj_release( &rec
->hdr
);
458 if (r
!= ERROR_SUCCESS
)
461 while (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
463 UINT sequence
= MSI_RecordGetInteger( rec
, 2 );
465 /* FIXME: we only use the max/min sequence numbers for now */
466 pos
->min
= min( pos
->min
, sequence
);
467 pos
->max
= max( pos
->max
, sequence
);
469 msiobj_release( &rec
->hdr
);
471 msiobj_release( &view
->hdr
);
474 static void patch_offset_get_files( MSIDATABASE
*db
, UINT last_sequence
, struct patch_offset_list
*pos
)
476 static const WCHAR query
[] = {
477 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
478 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
479 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
484 r
= MSI_DatabaseOpenViewW( db
, query
, &view
);
485 if (r
!= ERROR_SUCCESS
)
488 rec
= MSI_CreateRecord( 1 );
489 MSI_RecordSetInteger( rec
, 1, last_sequence
);
491 r
= MSI_ViewExecute( view
, rec
);
492 msiobj_release( &rec
->hdr
);
493 if (r
!= ERROR_SUCCESS
)
496 while (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
498 UINT attributes
= MSI_RecordGetInteger( rec
, 7 );
499 if (attributes
& msidbFileAttributesPatchAdded
)
501 struct patch_offset
*po
= msi_alloc( sizeof(struct patch_offset
) );
503 po
->name
= msi_dup_record_field( rec
, 1 );
504 po
->sequence
= MSI_RecordGetInteger( rec
, 8 );
505 pos
->min
= min( pos
->min
, po
->sequence
);
506 pos
->max
= max( pos
->max
, po
->sequence
);
507 list_add_tail( &pos
->files
, &po
->entry
);
510 msiobj_release( &rec
->hdr
);
512 msiobj_release( &view
->hdr
);
515 static UINT
patch_offset_modify_db( MSIDATABASE
*db
, struct patch_offset_list
*pos
)
517 static const WCHAR query
[] = {
518 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
519 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','>','=',' ','?',' ',
520 'A','N','D',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
521 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
522 struct patch_offset
*po
;
527 r
= MSI_DatabaseOpenViewW( db
, query
, &view
);
528 if (r
!= ERROR_SUCCESS
)
529 return ERROR_SUCCESS
;
531 rec
= MSI_CreateRecord( 2 );
532 MSI_RecordSetInteger( rec
, 1, pos
->min
);
533 MSI_RecordSetInteger( rec
, 2, pos
->max
);
535 r
= MSI_ViewExecute( view
, rec
);
536 msiobj_release( &rec
->hdr
);
537 if (r
!= ERROR_SUCCESS
)
540 LIST_FOR_EACH_ENTRY( po
, &pos
->files
, struct patch_offset
, entry
)
543 while ((r_fetch
= MSI_ViewFetch( view
, &rec
)) == ERROR_SUCCESS
)
545 const WCHAR
*file
= MSI_RecordGetString( rec
, 1 );
548 if (!strcmpiW( file
, po
->name
))
551 seq
= MSI_RecordGetInteger( rec
, 8 );
552 MSI_RecordSetInteger( rec
, 8, seq
+ pos
->offset_to_apply
);
553 r
= MSI_ViewModify( view
, MSIMODIFY_UPDATE
, rec
);
554 if (r
!= ERROR_SUCCESS
)
555 ERR("Failed to update offset for file %s\n", debugstr_w(file
));
556 msiobj_release( &rec
->hdr
);
559 msiobj_release( &rec
->hdr
);
561 if (r_fetch
!= ERROR_SUCCESS
) break;
565 msiobj_release( &view
->hdr
);
566 return ERROR_SUCCESS
;
569 static const WCHAR patch_media_query
[] = {
570 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
571 'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
572 'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
573 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
586 static UINT
add_patch_media( MSIPACKAGE
*package
, IStorage
*patch
)
588 static const WCHAR delete_query
[] = {
589 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
590 'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0};
591 static const WCHAR insert_query
[] = {
592 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ',
593 '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',',
594 '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',',
595 '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ',
596 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
600 struct list media_list
;
601 struct patch_media
*media
, *next
;
603 r
= MSI_DatabaseOpenViewW( package
->db
, patch_media_query
, &view
);
604 if (r
!= ERROR_SUCCESS
) return r
;
606 r
= MSI_ViewExecute( view
, 0 );
607 if (r
!= ERROR_SUCCESS
)
609 msiobj_release( &view
->hdr
);
610 TRACE("query failed %u\n", r
);
613 list_init( &media_list
);
614 while (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
616 disk_id
= MSI_RecordGetInteger( rec
, 1 );
617 TRACE("disk_id %u\n", disk_id
);
618 if (disk_id
>= MSI_INITIAL_MEDIA_TRANSFORM_DISKID
)
620 msiobj_release( &rec
->hdr
);
623 if (!(media
= msi_alloc( sizeof( *media
)))) goto done
;
624 media
->disk_id
= disk_id
;
625 media
->last_sequence
= MSI_RecordGetInteger( rec
, 2 );
626 media
->prompt
= msi_dup_record_field( rec
, 3 );
627 media
->cabinet
= msi_dup_record_field( rec
, 4 );
628 media
->volume
= msi_dup_record_field( rec
, 5 );
629 media
->source
= msi_dup_record_field( rec
, 6 );
631 list_add_tail( &media_list
, &media
->entry
);
632 msiobj_release( &rec
->hdr
);
634 LIST_FOR_EACH_ENTRY( media
, &media_list
, struct patch_media
, entry
)
636 MSIQUERY
*delete_view
, *insert_view
;
638 r
= MSI_DatabaseOpenViewW( package
->db
, delete_query
, &delete_view
);
639 if (r
!= ERROR_SUCCESS
) goto done
;
641 rec
= MSI_CreateRecord( 1 );
642 MSI_RecordSetInteger( rec
, 1, media
->disk_id
);
644 r
= MSI_ViewExecute( delete_view
, rec
);
645 msiobj_release( &delete_view
->hdr
);
646 msiobj_release( &rec
->hdr
);
647 if (r
!= ERROR_SUCCESS
) goto done
;
649 r
= MSI_DatabaseOpenViewW( package
->db
, insert_query
, &insert_view
);
650 if (r
!= ERROR_SUCCESS
) goto done
;
652 disk_id
= package
->db
->media_transform_disk_id
;
653 TRACE("disk id %u\n", disk_id
);
654 TRACE("last sequence %u\n", media
->last_sequence
);
655 TRACE("prompt %s\n", debugstr_w(media
->prompt
));
656 TRACE("cabinet %s\n", debugstr_w(media
->cabinet
));
657 TRACE("volume %s\n", debugstr_w(media
->volume
));
658 TRACE("source %s\n", debugstr_w(media
->source
));
660 rec
= MSI_CreateRecord( 6 );
661 MSI_RecordSetInteger( rec
, 1, disk_id
);
662 MSI_RecordSetInteger( rec
, 2, media
->last_sequence
);
663 MSI_RecordSetStringW( rec
, 3, media
->prompt
);
664 MSI_RecordSetStringW( rec
, 4, media
->cabinet
);
665 MSI_RecordSetStringW( rec
, 5, media
->volume
);
666 MSI_RecordSetStringW( rec
, 6, media
->source
);
668 r
= MSI_ViewExecute( insert_view
, rec
);
669 msiobj_release( &insert_view
->hdr
);
670 msiobj_release( &rec
->hdr
);
671 if (r
!= ERROR_SUCCESS
) goto done
;
673 r
= msi_add_cabinet_stream( package
, disk_id
, patch
, media
->cabinet
);
674 if (r
!= ERROR_SUCCESS
) WARN("failed to add cabinet stream %u\n", r
);
675 package
->db
->media_transform_disk_id
++;
679 msiobj_release( &view
->hdr
);
680 LIST_FOR_EACH_ENTRY_SAFE( media
, next
, &media_list
, struct patch_media
, entry
)
682 list_remove( &media
->entry
);
683 msi_free( media
->prompt
);
684 msi_free( media
->cabinet
);
685 msi_free( media
->volume
);
686 msi_free( media
->source
);
692 static UINT
set_patch_offsets( MSIDATABASE
*db
)
698 r
= MSI_DatabaseOpenViewW( db
, patch_media_query
, &view
);
699 if (r
!= ERROR_SUCCESS
)
702 r
= MSI_ViewExecute( view
, 0 );
703 if (r
!= ERROR_SUCCESS
)
706 while (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
708 UINT last_sequence
= MSI_RecordGetInteger( rec
, 2 );
709 struct patch_offset_list
*pos
;
711 /* FIXME: set/check Source field instead? */
712 if (last_sequence
>= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET
)
714 msiobj_release( &rec
->hdr
);
717 pos
= patch_offset_list_create();
718 patch_offset_get_files( db
, last_sequence
, pos
);
719 patch_offset_get_patches( db
, last_sequence
, pos
);
721 UINT offset
= db
->media_transform_offset
- pos
->min
;
722 last_sequence
= offset
+ pos
->max
;
724 /* FIXME: this is for the patch table, which is not yet properly transformed */
725 last_sequence
+= pos
->min
;
726 pos
->offset_to_apply
= offset
;
728 patch_offset_modify_db( db
, pos
);
730 MSI_RecordSetInteger( rec
, 2, last_sequence
);
731 r
= MSI_ViewModify( view
, MSIMODIFY_UPDATE
, rec
);
732 if (r
!= ERROR_SUCCESS
)
733 ERR("Failed to update Media table entry, expect breakage (%u)\n", r
);
734 db
->media_transform_offset
= last_sequence
+ 1;
736 patch_offset_list_free( pos
);
737 msiobj_release( &rec
->hdr
);
741 msiobj_release( &view
->hdr
);
745 static UINT
msi_apply_patch_db( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
, MSIPATCHINFO
*patch
)
747 UINT i
, r
= ERROR_SUCCESS
;
750 /* apply substorage transforms */
751 substorage
= msi_split_string( patch
->transforms
, ';' );
752 for (i
= 0; substorage
&& substorage
[i
] && r
== ERROR_SUCCESS
; i
++)
754 r
= apply_substorage_transform( package
, patch_db
, substorage
[i
] );
755 if (r
== ERROR_SUCCESS
)
757 add_patch_media( package
, patch_db
->storage
);
758 set_patch_offsets( package
->db
);
761 msi_free( substorage
);
762 if (r
!= ERROR_SUCCESS
)
765 patch_set_media_source_prop( package
);
767 patch
->state
= MSIPATCHSTATE_APPLIED
;
768 list_add_tail( &package
->patches
, &patch
->entry
);
769 return ERROR_SUCCESS
;
772 void msi_free_patchinfo( MSIPATCHINFO
*patch
)
774 msi_free( patch
->patchcode
);
775 msi_free( patch
->products
);
776 msi_free( patch
->transforms
);
777 msi_free( patch
->filename
);
778 msi_free( patch
->localfile
);
782 static UINT
msi_apply_patch_package( MSIPACKAGE
*package
, const WCHAR
*file
)
784 static const WCHAR dotmsp
[] = {'.','m','s','p',0};
785 MSIDATABASE
*patch_db
= NULL
;
786 WCHAR localfile
[MAX_PATH
];
788 MSIPATCHINFO
*patch
= NULL
;
789 UINT r
= ERROR_SUCCESS
;
791 TRACE("%p %s\n", package
, debugstr_w(file
));
793 r
= MSI_OpenDatabaseW( file
, MSIDBOPEN_READONLY
+ MSIDBOPEN_PATCHFILE
, &patch_db
);
794 if (r
!= ERROR_SUCCESS
)
796 ERR("failed to open patch collection %s\n", debugstr_w( file
) );
799 if (!(si
= MSI_GetSummaryInformationW( patch_db
->storage
, 0 )))
801 msiobj_release( &patch_db
->hdr
);
802 return ERROR_FUNCTION_FAILED
;
804 r
= msi_check_patch_applicable( package
, si
);
805 if (r
!= ERROR_SUCCESS
)
807 TRACE("patch not applicable\n");
811 r
= msi_parse_patch_summary( si
, &patch
);
812 if ( r
!= ERROR_SUCCESS
)
815 r
= msi_create_empty_local_file( localfile
, dotmsp
);
816 if ( r
!= ERROR_SUCCESS
)
819 r
= ERROR_OUTOFMEMORY
;
820 if (!(patch
->filename
= strdupW( file
))) goto done
;
821 if (!(patch
->localfile
= strdupW( localfile
))) goto done
;
823 r
= msi_apply_patch_db( package
, patch_db
, patch
);
824 if (r
!= ERROR_SUCCESS
) WARN("patch failed to apply %u\n", r
);
827 msiobj_release( &si
->hdr
);
828 msiobj_release( &patch_db
->hdr
);
829 if (patch
&& r
!= ERROR_SUCCESS
)
831 DeleteFileW( patch
->localfile
);
832 msi_free_patchinfo( patch
);
837 /* get the PATCH property, and apply all the patches it specifies */
838 UINT
msi_apply_patches( MSIPACKAGE
*package
)
840 LPWSTR patch_list
, *patches
;
841 UINT i
, r
= ERROR_SUCCESS
;
843 patch_list
= msi_dup_property( package
->db
, szPatch
);
845 TRACE("patches to be applied: %s\n", debugstr_w(patch_list
));
847 patches
= msi_split_string( patch_list
, ';' );
848 for (i
= 0; patches
&& patches
[i
] && r
== ERROR_SUCCESS
; i
++)
849 r
= msi_apply_patch_package( package
, patches
[i
] );
852 msi_free( patch_list
);
856 UINT
msi_apply_transforms( MSIPACKAGE
*package
)
858 static const WCHAR szTransforms
[] = {'T','R','A','N','S','F','O','R','M','S',0};
859 LPWSTR xform_list
, *xforms
;
860 UINT i
, r
= ERROR_SUCCESS
;
862 xform_list
= msi_dup_property( package
->db
, szTransforms
);
863 xforms
= msi_split_string( xform_list
, ';' );
865 for (i
= 0; xforms
&& xforms
[i
] && r
== ERROR_SUCCESS
; i
++)
867 if (xforms
[i
][0] == ':')
868 r
= apply_substorage_transform( package
, package
->db
, xforms
[i
] );
873 if (!PathIsRelativeW( xforms
[i
] )) transform
= xforms
[i
];
876 WCHAR
*p
= strrchrW( package
->PackagePath
, '\\' );
877 DWORD len
= p
- package
->PackagePath
+ 1;
879 if (!(transform
= msi_alloc( (len
+ strlenW( xforms
[i
] ) + 1) * sizeof(WCHAR
)) ))
882 msi_free( xform_list
);
883 return ERROR_OUTOFMEMORY
;
885 memcpy( transform
, package
->PackagePath
, len
* sizeof(WCHAR
) );
886 memcpy( transform
+ len
, xforms
[i
], (strlenW( xforms
[i
] ) + 1) * sizeof(WCHAR
) );
888 r
= MSI_DatabaseApplyTransformW( package
->db
, transform
, 0 );
889 if (transform
!= xforms
[i
]) msi_free( transform
);
893 msi_free( xform_list
);
897 UINT
msi_apply_registered_patch( MSIPACKAGE
*package
, LPCWSTR patch_code
)
901 WCHAR patch_file
[MAX_PATH
];
902 MSIDATABASE
*patch_db
;
903 MSIPATCHINFO
*patch_info
;
906 len
= sizeof(patch_file
) / sizeof(WCHAR
);
907 r
= MsiGetPatchInfoExW( patch_code
, package
->ProductCode
, NULL
, package
->Context
,
908 INSTALLPROPERTY_LOCALPACKAGEW
, patch_file
, &len
);
909 if (r
!= ERROR_SUCCESS
)
911 ERR("failed to get patch filename %u\n", r
);
914 r
= MSI_OpenDatabaseW( patch_file
, MSIDBOPEN_READONLY
+ MSIDBOPEN_PATCHFILE
, &patch_db
);
915 if (r
!= ERROR_SUCCESS
)
917 ERR("failed to open patch database %s\n", debugstr_w( patch_file
));
920 si
= MSI_GetSummaryInformationW( patch_db
->storage
, 0 );
923 msiobj_release( &patch_db
->hdr
);
924 return ERROR_FUNCTION_FAILED
;
926 r
= msi_parse_patch_summary( si
, &patch_info
);
927 msiobj_release( &si
->hdr
);
928 if (r
!= ERROR_SUCCESS
)
930 ERR("failed to parse patch summary %u\n", r
);
931 msiobj_release( &patch_db
->hdr
);
934 patch_info
->localfile
= strdupW( patch_file
);
935 if (!patch_info
->localfile
)
937 msiobj_release( &patch_db
->hdr
);
938 msi_free_patchinfo( patch_info
);
939 return ERROR_OUTOFMEMORY
;
941 r
= msi_apply_patch_db( package
, patch_db
, patch_info
);
942 msiobj_release( &patch_db
->hdr
);
943 if (r
!= ERROR_SUCCESS
)
945 ERR("failed to apply patch %u\n", r
);
946 msi_free_patchinfo( patch_info
);