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"
32 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
34 static BOOL
match_language( MSIPACKAGE
*package
, LANGID langid
)
38 if (!package
->num_langids
|| !langid
) return TRUE
;
39 for (i
= 0; i
< package
->num_langids
; i
++)
41 if (package
->langids
[i
] == langid
) return TRUE
;
48 WCHAR
*product_code_from
;
49 WCHAR
*product_code_to
;
55 static void free_transform_desc( struct transform_desc
*desc
)
57 free( desc
->product_code_from
);
58 free( desc
->product_code_to
);
59 free( desc
->version_from
);
60 free( desc
->version_to
);
61 free( desc
->upgrade_code
);
65 static struct transform_desc
*parse_transform_desc( const WCHAR
*str
)
67 struct transform_desc
*ret
;
68 const WCHAR
*p
= str
, *q
;
71 if (!(ret
= calloc( 1, sizeof(*ret
) ))) return NULL
;
74 if (*p
!= '{' || !q
) goto error
;
77 if (!(ret
->product_code_from
= malloc( (len
+ 1) * sizeof(WCHAR
) ))) goto error
;
78 memcpy( ret
->product_code_from
, p
, len
* sizeof(WCHAR
) );
79 ret
->product_code_from
[len
] = 0;
82 if (!(q
= wcschr( p
, ';' ))) goto error
;
84 if (!(ret
->version_from
= malloc( (len
+ 1) * sizeof(WCHAR
) ))) goto error
;
85 memcpy( ret
->version_from
, p
, len
* sizeof(WCHAR
) );
86 ret
->version_from
[len
] = 0;
90 if (*p
!= '{' || !q
) goto error
;
93 if (!(ret
->product_code_to
= malloc( (len
+ 1) * sizeof(WCHAR
) ))) goto error
;
94 memcpy( ret
->product_code_to
, p
, len
* sizeof(WCHAR
) );
95 ret
->product_code_to
[len
] = 0;
98 if (!(q
= wcschr( p
, ';' ))) goto error
;
100 if (!(ret
->version_to
= malloc( (len
+ 1) * sizeof(WCHAR
) ))) goto error
;
101 memcpy( ret
->version_to
, p
, len
* sizeof(WCHAR
) );
102 ret
->version_to
[len
] = 0;
105 q
= wcschr( p
, '}' );
106 if (*p
!= '{' || !q
) goto error
;
109 if (!(ret
->upgrade_code
= malloc( (len
+ 1) * sizeof(WCHAR
) ))) goto error
;
110 memcpy( ret
->upgrade_code
, p
, len
* sizeof(WCHAR
) );
111 ret
->upgrade_code
[len
] = 0;
116 free_transform_desc( ret
);
120 static UINT
check_transform_applicable( MSIPACKAGE
*package
, IStorage
*transform
)
122 static const UINT supported_flags
=
123 MSITRANSFORM_VALIDATE_PRODUCT
| MSITRANSFORM_VALIDATE_LANGUAGE
|
124 MSITRANSFORM_VALIDATE_PLATFORM
| MSITRANSFORM_VALIDATE_MAJORVERSION
|
125 MSITRANSFORM_VALIDATE_MINORVERSION
| MSITRANSFORM_VALIDATE_UPGRADECODE
;
127 UINT r
, valid_flags
= 0, wanted_flags
= 0;
128 WCHAR
*template, *product
, *p
;
129 struct transform_desc
*desc
;
131 r
= msi_get_suminfo( transform
, 0, &si
);
132 if (r
!= ERROR_SUCCESS
)
134 WARN("no summary information!\n");
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 /* native is not validating platform */
142 wanted_flags
&= ~MSITRANSFORM_VALIDATE_PLATFORM
;
144 if (wanted_flags
& ~supported_flags
)
146 FIXME("unsupported validation flags 0x%04x\n", wanted_flags
);
147 msiobj_release( &si
->hdr
);
148 return ERROR_FUNCTION_FAILED
;
150 if (!(template = msi_suminfo_dup_string( si
, PID_TEMPLATE
)))
152 WARN("no template property!\n");
153 msiobj_release( &si
->hdr
);
154 return ERROR_FUNCTION_FAILED
;
156 TRACE("template property: %s\n", debugstr_w(template));
157 if (!(product
= msi_get_suminfo_product( transform
)))
159 WARN("no product property!\n");
161 msiobj_release( &si
->hdr
);
162 return ERROR_FUNCTION_FAILED
;
164 TRACE("product property: %s\n", debugstr_w(product
));
165 if (!(desc
= parse_transform_desc( product
)))
168 msiobj_release( &si
->hdr
);
169 return ERROR_FUNCTION_FAILED
;
173 if (wanted_flags
& MSITRANSFORM_VALIDATE_LANGUAGE
)
175 if (!template[0] || ((p
= wcschr( template, ';' )) && match_language( package
, wcstol( p
+ 1, NULL
, 10 ) )))
177 valid_flags
|= MSITRANSFORM_VALIDATE_LANGUAGE
;
180 if (wanted_flags
& MSITRANSFORM_VALIDATE_PRODUCT
)
182 WCHAR
*product_code_installed
= msi_dup_property( package
->db
, L
"ProductCode" );
184 if (!product_code_installed
)
187 free_transform_desc( desc
);
188 msiobj_release( &si
->hdr
);
189 return ERROR_INSTALL_PACKAGE_INVALID
;
191 if (!wcscmp( desc
->product_code_from
, product_code_installed
))
193 valid_flags
|= MSITRANSFORM_VALIDATE_PRODUCT
;
195 free( product_code_installed
);
198 if (wanted_flags
& MSITRANSFORM_VALIDATE_MAJORVERSION
)
200 WCHAR
*product_version_installed
= msi_dup_property( package
->db
, L
"ProductVersion" );
201 DWORD major_installed
, minor_installed
, major
, minor
;
203 if (!product_version_installed
)
205 free_transform_desc( desc
);
206 msiobj_release( &si
->hdr
);
207 return ERROR_INSTALL_PACKAGE_INVALID
;
209 msi_parse_version_string( product_version_installed
, &major_installed
, &minor_installed
);
210 msi_parse_version_string( desc
->version_from
, &major
, &minor
);
212 if (major_installed
== major
)
214 valid_flags
|= MSITRANSFORM_VALIDATE_MAJORVERSION
;
215 wanted_flags
&= ~MSITRANSFORM_VALIDATE_MINORVERSION
;
217 free( product_version_installed
);
219 else if (wanted_flags
& MSITRANSFORM_VALIDATE_MINORVERSION
)
221 WCHAR
*product_version_installed
= msi_dup_property( package
->db
, L
"ProductVersion" );
222 DWORD major_installed
, minor_installed
, major
, minor
;
224 if (!product_version_installed
)
226 free_transform_desc( desc
);
227 msiobj_release( &si
->hdr
);
228 return ERROR_INSTALL_PACKAGE_INVALID
;
230 msi_parse_version_string( product_version_installed
, &major_installed
, &minor_installed
);
231 msi_parse_version_string( desc
->version_from
, &major
, &minor
);
233 if (major_installed
== major
&& minor_installed
== minor
)
234 valid_flags
|= MSITRANSFORM_VALIDATE_MINORVERSION
;
235 free( product_version_installed
);
237 if (wanted_flags
& MSITRANSFORM_VALIDATE_UPGRADECODE
)
239 WCHAR
*upgrade_code_installed
= msi_dup_property( package
->db
, L
"UpgradeCode" );
241 if (!upgrade_code_installed
)
243 free_transform_desc( desc
);
244 msiobj_release( &si
->hdr
);
245 return ERROR_INSTALL_PACKAGE_INVALID
;
247 if (!wcscmp( desc
->upgrade_code
, upgrade_code_installed
))
248 valid_flags
|= MSITRANSFORM_VALIDATE_UPGRADECODE
;
249 free( upgrade_code_installed
);
252 free_transform_desc( desc
);
253 msiobj_release( &si
->hdr
);
254 if ((valid_flags
& wanted_flags
) != wanted_flags
) return ERROR_FUNCTION_FAILED
;
255 TRACE("applicable transform\n");
256 return ERROR_SUCCESS
;
259 static UINT
apply_substorage_transform( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
, LPCWSTR name
)
261 UINT ret
= ERROR_FUNCTION_FAILED
;
262 IStorage
*stg
= NULL
;
265 TRACE("%p %s\n", package
, debugstr_w(name
));
269 ERR("expected a colon in %s\n", debugstr_w(name
));
270 return ERROR_FUNCTION_FAILED
;
272 r
= IStorage_OpenStorage( patch_db
->storage
, name
, NULL
, STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
275 ret
= check_transform_applicable( package
, stg
);
276 if (ret
== ERROR_SUCCESS
)
278 msi_table_apply_transform( package
->db
, stg
, MSITRANSFORM_ERROR_VIEWTRANSFORM
);
279 msi_table_apply_transform( package
->db
, stg
, 0 );
283 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name
));
285 IStorage_Release( stg
);
289 ERR("failed to open substorage %s\n", debugstr_w(name
));
291 return ERROR_SUCCESS
;
294 UINT
msi_check_patch_applicable( MSIPACKAGE
*package
, MSISUMMARYINFO
*si
)
296 LPWSTR guid_list
, *guids
, product_code
;
297 UINT i
, ret
= ERROR_FUNCTION_FAILED
;
299 product_code
= msi_dup_property( package
->db
, L
"ProductCode" );
302 /* FIXME: the property ProductCode should be written into the DB somewhere */
303 ERR("no product code to check\n");
304 return ERROR_SUCCESS
;
306 guid_list
= msi_suminfo_dup_string( si
, PID_TEMPLATE
);
307 guids
= msi_split_string( guid_list
, ';' );
308 for (i
= 0; guids
[i
] && ret
!= ERROR_SUCCESS
; i
++)
310 if (!wcscmp( guids
[i
], product_code
)) ret
= ERROR_SUCCESS
;
314 free( product_code
);
318 static UINT
parse_patch_summary( MSISUMMARYINFO
*si
, MSIPATCHINFO
**patch
)
321 UINT r
= ERROR_SUCCESS
;
324 if (!(pi
= calloc( 1, sizeof(MSIPATCHINFO
) )))
326 return ERROR_OUTOFMEMORY
;
328 if (!(pi
->patchcode
= msi_suminfo_dup_string( si
, PID_REVNUMBER
)))
331 return ERROR_OUTOFMEMORY
;
336 free( pi
->patchcode
);
338 return ERROR_PATCH_PACKAGE_INVALID
;
340 if (!(p
= wcschr( p
+ 1, '}' )))
342 free( pi
->patchcode
);
344 return ERROR_PATCH_PACKAGE_INVALID
;
348 FIXME("patch obsoletes %s\n", debugstr_w(p
+ 1));
351 TRACE("patch code %s\n", debugstr_w(pi
->patchcode
));
352 if (!(pi
->products
= msi_suminfo_dup_string( si
, PID_TEMPLATE
)))
354 free( pi
->patchcode
);
356 return ERROR_OUTOFMEMORY
;
358 if (!(pi
->transforms
= msi_suminfo_dup_string( si
, PID_LASTAUTHOR
)))
360 free( pi
->patchcode
);
361 free( pi
->products
);
363 return ERROR_OUTOFMEMORY
;
369 static UINT
patch_set_media_source_prop( MSIPACKAGE
*package
)
373 const WCHAR
*property
;
377 r
= MSI_DatabaseOpenViewW( package
->db
, L
"SELECT `Source` FROM `Media` WHERE `Source` IS NOT NULL", &view
);
378 if (r
!= ERROR_SUCCESS
)
381 r
= MSI_ViewExecute( view
, 0 );
382 if (r
!= ERROR_SUCCESS
)
385 if (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
387 property
= MSI_RecordGetString( rec
, 1 );
388 patch
= msi_dup_property( package
->db
, L
"PATCH" );
389 msi_set_property( package
->db
, property
, patch
, -1 );
391 msiobj_release( &rec
->hdr
);
395 msiobj_release( &view
->hdr
);
406 struct patch_offset_list
410 UINT count
, min
, max
;
411 UINT offset_to_apply
;
414 static struct patch_offset_list
*patch_offset_list_create( void )
416 struct patch_offset_list
*pos
= malloc( sizeof(struct patch_offset_list
) );
417 list_init( &pos
->files
);
418 list_init( &pos
->patches
);
419 pos
->count
= pos
->max
= 0;
424 static void patch_offset_list_free( struct patch_offset_list
*pos
)
426 struct patch_offset
*po
, *po2
;
428 LIST_FOR_EACH_ENTRY_SAFE( po
, po2
, &pos
->files
, struct patch_offset
, entry
)
433 LIST_FOR_EACH_ENTRY_SAFE( po
, po2
, &pos
->patches
, struct patch_offset
, entry
)
441 static void patch_offset_get_filepatches( MSIDATABASE
*db
, UINT last_sequence
, struct patch_offset_list
*pos
)
447 r
= MSI_DatabaseOpenViewW( db
, L
"SELECT * FROM `Patch` WHERE `Sequence` <= ? ORDER BY `Sequence`", &view
);
448 if (r
!= ERROR_SUCCESS
)
451 rec
= MSI_CreateRecord( 1 );
452 MSI_RecordSetInteger( rec
, 1, last_sequence
);
454 r
= MSI_ViewExecute( view
, rec
);
455 msiobj_release( &rec
->hdr
);
456 if (r
!= ERROR_SUCCESS
)
459 while (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
461 struct patch_offset
*po
= malloc( sizeof(struct patch_offset
) );
463 po
->name
= msi_dup_record_field( rec
, 1 );
464 po
->sequence
= MSI_RecordGetInteger( rec
, 2 );
465 pos
->min
= min( pos
->min
, po
->sequence
);
466 pos
->max
= max( pos
->max
, po
->sequence
);
467 list_add_tail( &pos
->patches
, &po
->entry
);
470 msiobj_release( &rec
->hdr
);
472 msiobj_release( &view
->hdr
);
475 static void patch_offset_get_files( MSIDATABASE
*db
, UINT last_sequence
, struct patch_offset_list
*pos
)
481 r
= MSI_DatabaseOpenViewW( db
, L
"SELECT * FROM `File` WHERE `Sequence` <= ? ORDER BY `Sequence`", &view
);
482 if (r
!= ERROR_SUCCESS
)
485 rec
= MSI_CreateRecord( 1 );
486 MSI_RecordSetInteger( rec
, 1, last_sequence
);
488 r
= MSI_ViewExecute( view
, rec
);
489 msiobj_release( &rec
->hdr
);
490 if (r
!= ERROR_SUCCESS
)
493 while (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
495 UINT attributes
= MSI_RecordGetInteger( rec
, 7 );
496 if (attributes
& msidbFileAttributesPatchAdded
)
498 struct patch_offset
*po
= malloc( sizeof(struct patch_offset
) );
500 po
->name
= msi_dup_record_field( rec
, 1 );
501 po
->sequence
= MSI_RecordGetInteger( rec
, 8 );
502 pos
->min
= min( pos
->min
, po
->sequence
);
503 pos
->max
= max( pos
->max
, po
->sequence
);
504 list_add_tail( &pos
->files
, &po
->entry
);
507 msiobj_release( &rec
->hdr
);
509 msiobj_release( &view
->hdr
);
512 static UINT
patch_update_file_sequence( MSIDATABASE
*db
, const struct patch_offset_list
*pos
,
513 MSIQUERY
*view
, MSIRECORD
*rec
)
515 struct patch_offset
*po
;
516 const WCHAR
*file
= MSI_RecordGetString( rec
, 1 );
517 UINT r
= ERROR_SUCCESS
, seq
= MSI_RecordGetInteger( rec
, 8 );
519 LIST_FOR_EACH_ENTRY( po
, &pos
->files
, struct patch_offset
, entry
)
521 if (!wcsicmp( file
, po
->name
))
523 MSI_RecordSetInteger( rec
, 8, seq
+ pos
->offset_to_apply
);
524 r
= MSI_ViewModify( view
, MSIMODIFY_UPDATE
, rec
);
525 if (r
!= ERROR_SUCCESS
)
526 ERR("Failed to update offset for file %s (%u)\n", debugstr_w(file
), r
);
533 static UINT
patch_update_filepatch_sequence( MSIDATABASE
*db
, const struct patch_offset_list
*pos
,
534 MSIQUERY
*view
, MSIRECORD
*rec
)
536 struct patch_offset
*po
;
537 const WCHAR
*file
= MSI_RecordGetString( rec
, 1 );
538 UINT r
= ERROR_SUCCESS
, seq
= MSI_RecordGetInteger( rec
, 2 );
540 LIST_FOR_EACH_ENTRY( po
, &pos
->patches
, struct patch_offset
, entry
)
542 if (seq
== po
->sequence
&& !wcsicmp( file
, po
->name
))
544 MSIQUERY
*delete_view
, *insert_view
;
547 r
= MSI_DatabaseOpenViewW( db
, L
"DELETE FROM `Patch` WHERE `File_` = ? AND `Sequence` = ?", &delete_view
);
548 if (r
!= ERROR_SUCCESS
) return r
;
550 rec2
= MSI_CreateRecord( 2 );
551 MSI_RecordSetStringW( rec2
, 1, po
->name
);
552 MSI_RecordSetInteger( rec2
, 2, po
->sequence
);
553 r
= MSI_ViewExecute( delete_view
, rec2
);
554 msiobj_release( &delete_view
->hdr
);
555 msiobj_release( &rec2
->hdr
);
556 if (r
!= ERROR_SUCCESS
) return r
;
558 r
= MSI_DatabaseOpenViewW( db
, L
"INSERT INTO `Patch` (`File_`,`Sequence`,`PatchSize`,`Attributes`,"
559 L
"`Header`,`StreamRef_`) VALUES (?,?,?,?,?,?)", &insert_view
);
560 if (r
!= ERROR_SUCCESS
) return r
;
562 MSI_RecordSetInteger( rec
, 2, po
->sequence
+ pos
->offset_to_apply
);
564 r
= MSI_ViewExecute( insert_view
, rec
);
565 msiobj_release( &insert_view
->hdr
);
566 if (r
!= ERROR_SUCCESS
)
567 ERR("Failed to update offset for filepatch %s (%u)\n", debugstr_w(file
), r
);
574 static UINT
patch_offset_modify_db( MSIDATABASE
*db
, struct patch_offset_list
*pos
)
578 UINT r
, min
= pos
->min
, max
= pos
->max
, r_fetch
;
580 r
= MSI_DatabaseOpenViewW( db
,
581 L
"SELECT * FROM `File` WHERE `Sequence` >= ? AND `Sequence` <= ? ORDER BY `Sequence`",
583 if (r
!= ERROR_SUCCESS
)
584 return ERROR_SUCCESS
;
586 rec
= MSI_CreateRecord( 2 );
587 MSI_RecordSetInteger( rec
, 1, min
);
588 MSI_RecordSetInteger( rec
, 2, max
);
590 r
= MSI_ViewExecute( view
, rec
);
591 msiobj_release( &rec
->hdr
);
592 if (r
!= ERROR_SUCCESS
)
595 while ((r_fetch
= MSI_ViewFetch( view
, &rec
)) == ERROR_SUCCESS
)
597 r
= patch_update_file_sequence( db
, pos
, view
, rec
);
598 msiobj_release( &rec
->hdr
);
599 if (r
!= ERROR_SUCCESS
) goto done
;
601 msiobj_release( &view
->hdr
);
603 r
= MSI_DatabaseOpenViewW( db
,
604 L
"SELECT *FROM `Patch` WHERE `Sequence` >= ? AND `Sequence` <= ? ORDER BY `Sequence`",
606 if (r
!= ERROR_SUCCESS
)
607 return ERROR_SUCCESS
;
609 rec
= MSI_CreateRecord( 2 );
610 MSI_RecordSetInteger( rec
, 1, min
);
611 MSI_RecordSetInteger( rec
, 2, max
);
613 r
= MSI_ViewExecute( view
, rec
);
614 msiobj_release( &rec
->hdr
);
615 if (r
!= ERROR_SUCCESS
)
618 while ((r_fetch
= MSI_ViewFetch( view
, &rec
)) == ERROR_SUCCESS
)
620 r
= patch_update_filepatch_sequence( db
, pos
, view
, rec
);
621 msiobj_release( &rec
->hdr
);
622 if (r
!= ERROR_SUCCESS
) goto done
;
626 msiobj_release( &view
->hdr
);
630 static const WCHAR patch_media_query
[] =
631 L
"SELECT * FROM `Media` WHERE `Source` IS NOT NULL AND `Cabinet` IS NOT NULL ORDER BY `DiskId`";
644 static UINT
patch_add_media( MSIPACKAGE
*package
, IStorage
*storage
, MSIPATCHINFO
*patch
)
649 struct list media_list
;
650 struct patch_media
*media
, *next
;
652 r
= MSI_DatabaseOpenViewW( package
->db
, patch_media_query
, &view
);
653 if (r
!= ERROR_SUCCESS
) return r
;
655 r
= MSI_ViewExecute( view
, 0 );
656 if (r
!= ERROR_SUCCESS
)
658 msiobj_release( &view
->hdr
);
659 TRACE("query failed %u\n", r
);
662 list_init( &media_list
);
663 while (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
665 disk_id
= MSI_RecordGetInteger( rec
, 1 );
666 TRACE("disk_id %u\n", disk_id
);
667 if (disk_id
>= MSI_INITIAL_MEDIA_TRANSFORM_DISKID
)
669 msiobj_release( &rec
->hdr
);
672 if (!(media
= malloc( sizeof( *media
))))
674 msiobj_release( &rec
->hdr
);
677 media
->disk_id
= disk_id
;
678 media
->last_sequence
= MSI_RecordGetInteger( rec
, 2 );
679 media
->prompt
= msi_dup_record_field( rec
, 3 );
680 media
->cabinet
= msi_dup_record_field( rec
, 4 );
681 media
->volume
= msi_dup_record_field( rec
, 5 );
682 media
->source
= msi_dup_record_field( rec
, 6 );
684 list_add_tail( &media_list
, &media
->entry
);
685 msiobj_release( &rec
->hdr
);
687 LIST_FOR_EACH_ENTRY( media
, &media_list
, struct patch_media
, entry
)
689 MSIQUERY
*delete_view
, *insert_view
;
691 r
= MSI_DatabaseOpenViewW( package
->db
, L
"DELETE FROM `Media` WHERE `DiskId`=?", &delete_view
);
692 if (r
!= ERROR_SUCCESS
) goto done
;
694 rec
= MSI_CreateRecord( 1 );
695 MSI_RecordSetInteger( rec
, 1, media
->disk_id
);
697 r
= MSI_ViewExecute( delete_view
, rec
);
698 msiobj_release( &delete_view
->hdr
);
699 msiobj_release( &rec
->hdr
);
700 if (r
!= ERROR_SUCCESS
) goto done
;
702 r
= MSI_DatabaseOpenViewW( package
->db
, L
"INSERT INTO `Media` (`DiskId`,`LastSequence`,`DiskPrompt`,"
703 L
"`Cabinet`,`VolumeLabel`,`Source`) VALUES (?,?,?,?,?,?)",
705 if (r
!= ERROR_SUCCESS
) goto done
;
707 disk_id
= package
->db
->media_transform_disk_id
;
708 TRACE("disk id %u\n", disk_id
);
709 TRACE("last sequence %u\n", media
->last_sequence
);
710 TRACE("prompt %s\n", debugstr_w(media
->prompt
));
711 TRACE("cabinet %s\n", debugstr_w(media
->cabinet
));
712 TRACE("volume %s\n", debugstr_w(media
->volume
));
713 TRACE("source %s\n", debugstr_w(media
->source
));
715 rec
= MSI_CreateRecord( 6 );
716 MSI_RecordSetInteger( rec
, 1, disk_id
);
717 MSI_RecordSetInteger( rec
, 2, media
->last_sequence
);
718 MSI_RecordSetStringW( rec
, 3, media
->prompt
);
719 MSI_RecordSetStringW( rec
, 4, media
->cabinet
);
720 MSI_RecordSetStringW( rec
, 5, media
->volume
);
721 MSI_RecordSetStringW( rec
, 6, media
->source
);
723 r
= MSI_ViewExecute( insert_view
, rec
);
724 msiobj_release( &insert_view
->hdr
);
725 msiobj_release( &rec
->hdr
);
726 if (r
!= ERROR_SUCCESS
) goto done
;
728 r
= msi_add_cabinet_stream( package
, disk_id
, storage
, media
->cabinet
);
729 if (r
!= ERROR_SUCCESS
) ERR("failed to add cabinet stream %u\n", r
);
732 patch
->disk_id
= disk_id
;
733 package
->db
->media_transform_disk_id
++;
738 msiobj_release( &view
->hdr
);
739 LIST_FOR_EACH_ENTRY_SAFE( media
, next
, &media_list
, struct patch_media
, entry
)
741 list_remove( &media
->entry
);
742 free( media
->prompt
);
743 free( media
->cabinet
);
744 free( media
->volume
);
745 free( media
->source
);
751 static UINT
patch_set_offsets( MSIDATABASE
*db
, MSIPATCHINFO
*patch
)
757 r
= MSI_DatabaseOpenViewW( db
, patch_media_query
, &view
);
758 if (r
!= ERROR_SUCCESS
)
761 r
= MSI_ViewExecute( view
, 0 );
762 if (r
!= ERROR_SUCCESS
)
765 while (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
767 UINT offset
, last_sequence
= MSI_RecordGetInteger( rec
, 2 );
768 struct patch_offset_list
*pos
;
770 /* FIXME: set/check Source field instead? */
771 if (last_sequence
>= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET
)
773 msiobj_release( &rec
->hdr
);
776 pos
= patch_offset_list_create();
777 patch_offset_get_files( db
, last_sequence
, pos
);
778 patch_offset_get_filepatches( db
, last_sequence
, pos
);
780 offset
= db
->media_transform_offset
- pos
->min
;
781 last_sequence
= offset
+ pos
->max
;
783 last_sequence
+= pos
->min
;
784 pos
->offset_to_apply
= offset
;
787 r
= patch_offset_modify_db( db
, pos
);
788 if (r
!= ERROR_SUCCESS
)
789 ERR("Failed to set offsets, expect breakage (%u)\n", r
);
791 MSI_RecordSetInteger( rec
, 2, last_sequence
);
792 r
= MSI_ViewModify( view
, MSIMODIFY_UPDATE
, rec
);
793 if (r
!= ERROR_SUCCESS
)
794 ERR("Failed to update Media table entry, expect breakage (%u)\n", r
);
796 db
->media_transform_offset
= last_sequence
+ 1;
798 patch_offset_list_free( pos
);
799 msiobj_release( &rec
->hdr
);
803 msiobj_release( &view
->hdr
);
807 static DWORD
is_uninstallable( MSIDATABASE
*db
)
813 if (MSI_DatabaseOpenViewW( db
, L
"SELECT `Value` FROM `MsiPatchMetadata` WHERE `Company` IS NULL "
814 L
"AND `Property`='AllowRemoval'", &view
) != ERROR_SUCCESS
) return 0;
815 if (MSI_ViewExecute( view
, 0 ) != ERROR_SUCCESS
)
817 msiobj_release( &view
->hdr
);
821 if (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
823 const WCHAR
*value
= MSI_RecordGetString( rec
, 1 );
824 ret
= wcstol( value
, NULL
, 10 );
825 msiobj_release( &rec
->hdr
);
828 FIXME( "check other criteria\n" );
830 msiobj_release( &view
->hdr
);
834 static UINT
apply_patch_db( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
, MSIPATCHINFO
*patch
)
836 UINT i
, r
= ERROR_SUCCESS
;
839 /* apply substorage transforms */
840 substorage
= msi_split_string( patch
->transforms
, ';' );
841 for (i
= 0; substorage
&& substorage
[i
] && r
== ERROR_SUCCESS
; i
++)
843 r
= apply_substorage_transform( package
, patch_db
, substorage
[i
] );
844 if (r
== ERROR_SUCCESS
)
846 r
= patch_set_offsets( package
->db
, patch
);
847 if (r
== ERROR_SUCCESS
)
848 r
= patch_add_media( package
, patch_db
->storage
, patch
);
852 if (r
!= ERROR_SUCCESS
)
855 r
= patch_set_media_source_prop( package
);
856 if (r
!= ERROR_SUCCESS
)
859 patch
->uninstallable
= is_uninstallable( patch_db
);
860 patch
->state
= MSIPATCHSTATE_APPLIED
;
861 list_add_tail( &package
->patches
, &patch
->entry
);
862 return ERROR_SUCCESS
;
865 void msi_free_patchinfo( MSIPATCHINFO
*patch
)
867 free( patch
->patchcode
);
868 free( patch
->products
);
869 free( patch
->transforms
);
870 free( patch
->filename
);
871 free( patch
->localfile
);
875 static UINT
apply_patch_package( MSIPACKAGE
*package
, const WCHAR
*file
)
877 MSIDATABASE
*patch_db
= NULL
;
878 WCHAR localfile
[MAX_PATH
];
880 MSIPATCHINFO
*patch
= NULL
;
883 TRACE("%p, %s\n", package
, debugstr_w(file
));
885 r
= MSI_OpenDatabaseW( file
, MSIDBOPEN_READONLY
+ MSIDBOPEN_PATCHFILE
, &patch_db
);
886 if (r
!= ERROR_SUCCESS
)
888 ERR("failed to open patch collection %s\n", debugstr_w( file
) );
891 r
= msi_get_suminfo( patch_db
->storage
, 0, &si
);
892 if (r
!= ERROR_SUCCESS
)
894 msiobj_release( &patch_db
->hdr
);
897 r
= msi_check_patch_applicable( package
, si
);
898 if (r
!= ERROR_SUCCESS
)
900 TRACE("patch not applicable\n");
904 r
= parse_patch_summary( si
, &patch
);
905 if ( r
!= ERROR_SUCCESS
)
908 r
= msi_create_empty_local_file( localfile
, L
".msp" );
909 if ( r
!= ERROR_SUCCESS
)
912 r
= ERROR_OUTOFMEMORY
;
913 patch
->registered
= FALSE
;
914 if (!(patch
->filename
= wcsdup( file
))) goto done
;
915 if (!(patch
->localfile
= wcsdup( localfile
))) goto done
;
917 r
= apply_patch_db( package
, patch_db
, patch
);
918 if (r
!= ERROR_SUCCESS
) WARN("patch failed to apply %u\n", r
);
921 msiobj_release( &si
->hdr
);
922 msiobj_release( &patch_db
->hdr
);
923 if (patch
&& r
!= ERROR_SUCCESS
)
925 DeleteFileW( patch
->localfile
);
926 msi_free_patchinfo( patch
);
931 /* get the PATCH property, and apply all the patches it specifies */
932 UINT
msi_apply_patches( MSIPACKAGE
*package
)
934 LPWSTR patch_list
, *patches
;
935 UINT i
, r
= ERROR_SUCCESS
;
937 patch_list
= msi_dup_property( package
->db
, L
"PATCH" );
939 TRACE("patches to be applied: %s\n", debugstr_w(patch_list
));
941 patches
= msi_split_string( patch_list
, ';' );
942 for (i
= 0; patches
&& patches
[i
] && r
== ERROR_SUCCESS
; i
++)
943 r
= apply_patch_package( package
, patches
[i
] );
950 UINT
msi_apply_transforms( MSIPACKAGE
*package
)
952 LPWSTR xform_list
, *xforms
;
953 UINT i
, r
= ERROR_SUCCESS
;
955 xform_list
= msi_dup_property( package
->db
, L
"TRANSFORMS" );
956 xforms
= msi_split_string( xform_list
, ';' );
958 for (i
= 0; xforms
&& xforms
[i
] && r
== ERROR_SUCCESS
; i
++)
960 if (xforms
[i
][0] == ':')
961 r
= apply_substorage_transform( package
, package
->db
, xforms
[i
] );
966 if (!PathIsRelativeW( xforms
[i
] )) transform
= xforms
[i
];
969 WCHAR
*p
= wcsrchr( package
->PackagePath
, '\\' );
970 DWORD len
= p
- package
->PackagePath
+ 1;
972 if (!(transform
= malloc( (len
+ wcslen( xforms
[i
] ) + 1) * sizeof(WCHAR
)) ))
976 return ERROR_OUTOFMEMORY
;
978 memcpy( transform
, package
->PackagePath
, len
* sizeof(WCHAR
) );
979 memcpy( transform
+ len
, xforms
[i
], (lstrlenW( xforms
[i
] ) + 1) * sizeof(WCHAR
) );
981 r
= MSI_DatabaseApplyTransformW( package
->db
, transform
, 0 );
982 if (transform
!= xforms
[i
]) free( transform
);
990 UINT
msi_apply_registered_patch( MSIPACKAGE
*package
, LPCWSTR patch_code
)
994 WCHAR patch_file
[MAX_PATH
];
995 MSIDATABASE
*patch_db
;
996 MSIPATCHINFO
*patch_info
;
999 TRACE("%p, %s\n", package
, debugstr_w(patch_code
));
1001 len
= ARRAY_SIZE( patch_file
);
1002 r
= MsiGetPatchInfoExW( patch_code
, package
->ProductCode
, NULL
, package
->Context
,
1003 INSTALLPROPERTY_LOCALPACKAGEW
, patch_file
, &len
);
1004 if (r
!= ERROR_SUCCESS
)
1006 ERR("failed to get patch filename %u\n", r
);
1009 r
= MSI_OpenDatabaseW( patch_file
, MSIDBOPEN_READONLY
+ MSIDBOPEN_PATCHFILE
, &patch_db
);
1010 if (r
!= ERROR_SUCCESS
)
1012 ERR("failed to open patch database %s\n", debugstr_w( patch_file
));
1015 r
= msi_get_suminfo( patch_db
->storage
, 0, &si
);
1016 if (r
!= ERROR_SUCCESS
)
1018 msiobj_release( &patch_db
->hdr
);
1021 r
= parse_patch_summary( si
, &patch_info
);
1022 msiobj_release( &si
->hdr
);
1023 if (r
!= ERROR_SUCCESS
)
1025 ERR("failed to parse patch summary %u\n", r
);
1026 msiobj_release( &patch_db
->hdr
);
1029 patch_info
->registered
= TRUE
;
1030 patch_info
->localfile
= wcsdup( patch_file
);
1031 if (!patch_info
->localfile
)
1033 msiobj_release( &patch_db
->hdr
);
1034 msi_free_patchinfo( patch_info
);
1035 return ERROR_OUTOFMEMORY
;
1037 r
= apply_patch_db( package
, patch_db
, patch_info
);
1038 msiobj_release( &patch_db
->hdr
);
1039 if (r
!= ERROR_SUCCESS
)
1041 ERR("failed to apply patch %u\n", r
);
1042 msi_free_patchinfo( patch_info
);