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
24 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
26 static BOOL
match_language( MSIPACKAGE
*package
, LANGID langid
)
30 if (!package
->num_langids
|| !langid
) return TRUE
;
31 for (i
= 0; i
< package
->num_langids
; i
++)
33 if (package
->langids
[i
] == langid
) return TRUE
;
40 WCHAR
*product_code_from
;
41 WCHAR
*product_code_to
;
47 static void free_transform_desc( struct transform_desc
*desc
)
49 msi_free( desc
->product_code_from
);
50 msi_free( desc
->product_code_to
);
51 msi_free( desc
->version_from
);
52 msi_free( desc
->version_to
);
53 msi_free( desc
->upgrade_code
);
57 static struct transform_desc
*parse_transform_desc( const WCHAR
*str
)
59 struct transform_desc
*ret
;
60 const WCHAR
*p
= str
, *q
;
63 if (!(ret
= msi_alloc_zero( sizeof(*ret
) ))) return NULL
;
65 q
= strchrW( p
, '}' );
66 if (*p
!= '{' || !q
) goto error
;
69 if (!(ret
->product_code_from
= msi_alloc( (len
+ 1) * sizeof(WCHAR
) ))) goto error
;
70 memcpy( ret
->product_code_from
, p
, len
* sizeof(WCHAR
) );
71 ret
->product_code_from
[len
] = 0;
74 if (!(q
= strchrW( p
, ';' ))) goto error
;
76 if (!(ret
->version_from
= msi_alloc( (len
+ 1) * sizeof(WCHAR
) ))) goto error
;
77 memcpy( ret
->version_from
, p
, len
* sizeof(WCHAR
) );
78 ret
->version_from
[len
] = 0;
81 q
= strchrW( p
, '}' );
82 if (*p
!= '{' || !q
) goto error
;
85 if (!(ret
->product_code_to
= msi_alloc( (len
+ 1) * sizeof(WCHAR
) ))) goto error
;
86 memcpy( ret
->product_code_to
, p
, len
* sizeof(WCHAR
) );
87 ret
->product_code_to
[len
] = 0;
90 if (!(q
= strchrW( p
, ';' ))) goto error
;
92 if (!(ret
->version_to
= msi_alloc( (len
+ 1) * sizeof(WCHAR
) ))) goto error
;
93 memcpy( ret
->version_to
, p
, len
* sizeof(WCHAR
) );
94 ret
->version_to
[len
] = 0;
97 q
= strchrW( p
, '}' );
98 if (*p
!= '{' || !q
) goto error
;
101 if (!(ret
->upgrade_code
= msi_alloc( (len
+ 1) * sizeof(WCHAR
) ))) goto error
;
102 memcpy( ret
->upgrade_code
, p
, len
* sizeof(WCHAR
) );
103 ret
->upgrade_code
[len
] = 0;
108 free_transform_desc( ret
);
112 static UINT
check_transform_applicable( MSIPACKAGE
*package
, IStorage
*transform
)
114 static const UINT supported_flags
=
115 MSITRANSFORM_VALIDATE_PRODUCT
| MSITRANSFORM_VALIDATE_LANGUAGE
|
116 MSITRANSFORM_VALIDATE_PLATFORM
| MSITRANSFORM_VALIDATE_MAJORVERSION
|
117 MSITRANSFORM_VALIDATE_MINORVERSION
| MSITRANSFORM_VALIDATE_UPGRADECODE
;
119 UINT r
, valid_flags
= 0, wanted_flags
= 0;
120 WCHAR
*template, *product
, *p
;
121 struct transform_desc
*desc
;
123 r
= msi_get_suminfo( transform
, 0, &si
);
124 if (r
!= ERROR_SUCCESS
)
126 WARN("no summary information!\n");
129 wanted_flags
= msi_suminfo_get_int32( si
, PID_CHARCOUNT
);
130 wanted_flags
&= 0xffff; /* mask off error condition flags */
131 TRACE("validation flags 0x%04x\n", wanted_flags
);
133 if (wanted_flags
& ~supported_flags
)
135 FIXME("unsupported validation flags 0x%04x\n", wanted_flags
);
136 msiobj_release( &si
->hdr
);
137 return ERROR_FUNCTION_FAILED
;
139 if (!(template = msi_suminfo_dup_string( si
, PID_TEMPLATE
)))
141 WARN("no template property!\n");
142 msiobj_release( &si
->hdr
);
143 return ERROR_FUNCTION_FAILED
;
145 TRACE("template property: %s\n", debugstr_w(template));
146 if (!(product
= msi_get_suminfo_product( transform
)))
148 WARN("no product property!\n");
149 msi_free( template );
150 msiobj_release( &si
->hdr
);
151 return ERROR_FUNCTION_FAILED
;
153 TRACE("product property: %s\n", debugstr_w(product
));
154 if (!(desc
= parse_transform_desc( product
)))
156 msi_free( template );
157 msiobj_release( &si
->hdr
);
158 return ERROR_FUNCTION_FAILED
;
162 if (wanted_flags
& MSITRANSFORM_VALIDATE_LANGUAGE
)
164 if (!template[0] || ((p
= strchrW( template, ';' )) && match_language( package
, atoiW( p
+ 1 ) )))
166 valid_flags
|= MSITRANSFORM_VALIDATE_LANGUAGE
;
169 if (wanted_flags
& MSITRANSFORM_VALIDATE_PRODUCT
)
171 WCHAR
*product_code_installed
= msi_dup_property( package
->db
, szProductCode
);
173 if (!product_code_installed
)
175 msi_free( template );
176 free_transform_desc( desc
);
177 msiobj_release( &si
->hdr
);
178 return ERROR_INSTALL_PACKAGE_INVALID
;
180 if (!strcmpW( desc
->product_code_from
, product_code_installed
))
182 valid_flags
|= MSITRANSFORM_VALIDATE_PRODUCT
;
184 msi_free( product_code_installed
);
186 if (wanted_flags
& MSITRANSFORM_VALIDATE_PLATFORM
)
188 if ((p
= strchrW( template, ';' )))
191 if (package
->platform
== parse_platform( template ))
192 valid_flags
|= MSITRANSFORM_VALIDATE_PLATFORM
;
195 msi_free( template );
196 if (wanted_flags
& MSITRANSFORM_VALIDATE_MAJORVERSION
)
198 WCHAR
*product_version_installed
= msi_dup_property( package
->db
, szProductVersion
);
199 DWORD major_installed
, minor_installed
, major
, minor
;
201 if (!product_version_installed
)
203 free_transform_desc( desc
);
204 msiobj_release( &si
->hdr
);
205 return ERROR_INSTALL_PACKAGE_INVALID
;
207 msi_parse_version_string( product_version_installed
, &major_installed
, &minor_installed
);
208 msi_parse_version_string( desc
->version_from
, &major
, &minor
);
210 if (major_installed
== major
)
212 valid_flags
|= MSITRANSFORM_VALIDATE_MAJORVERSION
;
213 wanted_flags
&= ~MSITRANSFORM_VALIDATE_MINORVERSION
;
215 msi_free( product_version_installed
);
217 else if (wanted_flags
& MSITRANSFORM_VALIDATE_MINORVERSION
)
219 WCHAR
*product_version_installed
= msi_dup_property( package
->db
, szProductVersion
);
220 DWORD major_installed
, minor_installed
, major
, minor
;
222 if (!product_version_installed
)
224 free_transform_desc( desc
);
225 msiobj_release( &si
->hdr
);
226 return ERROR_INSTALL_PACKAGE_INVALID
;
228 msi_parse_version_string( product_version_installed
, &major_installed
, &minor_installed
);
229 msi_parse_version_string( desc
->version_from
, &major
, &minor
);
231 if (major_installed
== major
&& minor_installed
== minor
)
232 valid_flags
|= MSITRANSFORM_VALIDATE_MINORVERSION
;
233 msi_free( product_version_installed
);
235 if (wanted_flags
& MSITRANSFORM_VALIDATE_UPGRADECODE
)
237 WCHAR
*upgrade_code_installed
= msi_dup_property( package
->db
, szUpgradeCode
);
239 if (!upgrade_code_installed
)
241 free_transform_desc( desc
);
242 msiobj_release( &si
->hdr
);
243 return ERROR_INSTALL_PACKAGE_INVALID
;
245 if (!strcmpW( desc
->upgrade_code
, upgrade_code_installed
))
246 valid_flags
|= MSITRANSFORM_VALIDATE_UPGRADECODE
;
247 msi_free( upgrade_code_installed
);
250 free_transform_desc( desc
);
251 msiobj_release( &si
->hdr
);
252 if ((valid_flags
& wanted_flags
) != wanted_flags
) return ERROR_FUNCTION_FAILED
;
253 TRACE("applicable transform\n");
254 return ERROR_SUCCESS
;
257 static UINT
apply_substorage_transform( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
, LPCWSTR name
)
259 UINT ret
= ERROR_FUNCTION_FAILED
;
260 IStorage
*stg
= NULL
;
263 TRACE("%p %s\n", package
, debugstr_w(name
));
267 ERR("expected a colon in %s\n", debugstr_w(name
));
268 return ERROR_FUNCTION_FAILED
;
270 r
= IStorage_OpenStorage( patch_db
->storage
, name
, NULL
, STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
273 ret
= check_transform_applicable( package
, stg
);
274 if (ret
== ERROR_SUCCESS
)
275 msi_table_apply_transform( package
->db
, stg
);
277 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name
));
278 IStorage_Release( stg
);
282 ERR("failed to open substorage %s\n", debugstr_w(name
));
284 return ERROR_SUCCESS
;
287 UINT
msi_check_patch_applicable( MSIPACKAGE
*package
, MSISUMMARYINFO
*si
)
289 LPWSTR guid_list
, *guids
, product_code
;
290 UINT i
, ret
= ERROR_FUNCTION_FAILED
;
292 product_code
= msi_dup_property( package
->db
, szProductCode
);
295 /* FIXME: the property ProductCode should be written into the DB somewhere */
296 ERR("no product code to check\n");
297 return ERROR_SUCCESS
;
299 guid_list
= msi_suminfo_dup_string( si
, PID_TEMPLATE
);
300 guids
= msi_split_string( guid_list
, ';' );
301 for (i
= 0; guids
[i
] && ret
!= ERROR_SUCCESS
; i
++)
303 if (!strcmpW( guids
[i
], product_code
)) ret
= ERROR_SUCCESS
;
306 msi_free( guid_list
);
307 msi_free( product_code
);
311 static UINT
msi_parse_patch_summary( MSISUMMARYINFO
*si
, MSIPATCHINFO
**patch
)
314 UINT r
= ERROR_SUCCESS
;
317 if (!(pi
= msi_alloc_zero( sizeof(MSIPATCHINFO
) )))
319 return ERROR_OUTOFMEMORY
;
321 if (!(pi
->patchcode
= msi_suminfo_dup_string( si
, PID_REVNUMBER
)))
324 return ERROR_OUTOFMEMORY
;
329 msi_free( pi
->patchcode
);
331 return ERROR_PATCH_PACKAGE_INVALID
;
333 if (!(p
= strchrW( p
+ 1, '}' )))
335 msi_free( pi
->patchcode
);
337 return ERROR_PATCH_PACKAGE_INVALID
;
341 FIXME("patch obsoletes %s\n", debugstr_w(p
+ 1));
344 TRACE("patch code %s\n", debugstr_w(pi
->patchcode
));
345 if (!(pi
->products
= msi_suminfo_dup_string( si
, PID_TEMPLATE
)))
347 msi_free( pi
->patchcode
);
349 return ERROR_OUTOFMEMORY
;
351 if (!(pi
->transforms
= msi_suminfo_dup_string( si
, PID_LASTAUTHOR
)))
353 msi_free( pi
->patchcode
);
354 msi_free( pi
->products
);
356 return ERROR_OUTOFMEMORY
;
362 static UINT
patch_set_media_source_prop( MSIPACKAGE
*package
)
364 static const WCHAR query
[] = {
365 'S','E','L','E','C','T',' ','`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
366 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ',
367 'I','S',' ','N','O','T',' ','N','U','L','L',0};
370 const WCHAR
*property
;
374 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
375 if (r
!= ERROR_SUCCESS
)
378 r
= MSI_ViewExecute( view
, 0 );
379 if (r
!= ERROR_SUCCESS
)
382 if (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
384 property
= MSI_RecordGetString( rec
, 1 );
385 patch
= msi_dup_property( package
->db
, szPatch
);
386 msi_set_property( package
->db
, property
, patch
, -1 );
388 msiobj_release( &rec
->hdr
);
392 msiobj_release( &view
->hdr
);
403 struct patch_offset_list
407 UINT count
, min
, max
;
408 UINT offset_to_apply
;
411 static struct patch_offset_list
*patch_offset_list_create( void )
413 struct patch_offset_list
*pos
= msi_alloc( sizeof(struct patch_offset_list
) );
414 list_init( &pos
->files
);
415 list_init( &pos
->patches
);
416 pos
->count
= pos
->max
= 0;
421 static void patch_offset_list_free( struct patch_offset_list
*pos
)
423 struct patch_offset
*po
, *po2
;
425 LIST_FOR_EACH_ENTRY_SAFE( po
, po2
, &pos
->files
, struct patch_offset
, entry
)
427 msi_free( po
->name
);
430 LIST_FOR_EACH_ENTRY_SAFE( po
, po2
, &pos
->patches
, struct patch_offset
, entry
)
432 msi_free( po
->name
);
438 static void patch_offset_get_filepatches( MSIDATABASE
*db
, UINT last_sequence
, struct patch_offset_list
*pos
)
440 static const WCHAR query
[] = {
441 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
442 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
443 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
448 r
= MSI_DatabaseOpenViewW( db
, query
, &view
);
449 if (r
!= ERROR_SUCCESS
)
452 rec
= MSI_CreateRecord( 1 );
453 MSI_RecordSetInteger( rec
, 1, last_sequence
);
455 r
= MSI_ViewExecute( view
, rec
);
456 msiobj_release( &rec
->hdr
);
457 if (r
!= ERROR_SUCCESS
)
460 while (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
462 struct patch_offset
*po
= msi_alloc( sizeof(struct patch_offset
) );
464 po
->name
= msi_dup_record_field( rec
, 1 );
465 po
->sequence
= MSI_RecordGetInteger( rec
, 2 );
466 pos
->min
= min( pos
->min
, po
->sequence
);
467 pos
->max
= max( pos
->max
, po
->sequence
);
468 list_add_tail( &pos
->patches
, &po
->entry
);
471 msiobj_release( &rec
->hdr
);
473 msiobj_release( &view
->hdr
);
476 static void patch_offset_get_files( MSIDATABASE
*db
, UINT last_sequence
, struct patch_offset_list
*pos
)
478 static const WCHAR query
[] = {
479 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
480 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
481 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
486 r
= MSI_DatabaseOpenViewW( db
, query
, &view
);
487 if (r
!= ERROR_SUCCESS
)
490 rec
= MSI_CreateRecord( 1 );
491 MSI_RecordSetInteger( rec
, 1, last_sequence
);
493 r
= MSI_ViewExecute( view
, rec
);
494 msiobj_release( &rec
->hdr
);
495 if (r
!= ERROR_SUCCESS
)
498 while (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
500 UINT attributes
= MSI_RecordGetInteger( rec
, 7 );
501 if (attributes
& msidbFileAttributesPatchAdded
)
503 struct patch_offset
*po
= msi_alloc( sizeof(struct patch_offset
) );
505 po
->name
= msi_dup_record_field( rec
, 1 );
506 po
->sequence
= MSI_RecordGetInteger( rec
, 8 );
507 pos
->min
= min( pos
->min
, po
->sequence
);
508 pos
->max
= max( pos
->max
, po
->sequence
);
509 list_add_tail( &pos
->files
, &po
->entry
);
512 msiobj_release( &rec
->hdr
);
514 msiobj_release( &view
->hdr
);
517 static UINT
patch_update_file_sequence( MSIDATABASE
*db
, const struct patch_offset_list
*pos
,
518 MSIQUERY
*view
, MSIRECORD
*rec
)
520 struct patch_offset
*po
;
521 const WCHAR
*file
= MSI_RecordGetString( rec
, 1 );
522 UINT r
= ERROR_SUCCESS
, seq
= MSI_RecordGetInteger( rec
, 8 );
524 LIST_FOR_EACH_ENTRY( po
, &pos
->files
, struct patch_offset
, entry
)
526 if (!strcmpiW( file
, po
->name
))
528 MSI_RecordSetInteger( rec
, 8, seq
+ pos
->offset_to_apply
);
529 r
= MSI_ViewModify( view
, MSIMODIFY_UPDATE
, rec
);
530 if (r
!= ERROR_SUCCESS
)
531 ERR("Failed to update offset for file %s (%u)\n", debugstr_w(file
), r
);
538 static UINT
patch_update_filepatch_sequence( MSIDATABASE
*db
, const struct patch_offset_list
*pos
,
539 MSIQUERY
*view
, MSIRECORD
*rec
)
541 static const WCHAR delete_query
[] = {
542 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','P','a','t','c','h','`',' ',
543 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','?',' ',
544 'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','=',' ','?',0};
545 static const WCHAR insert_query
[] = {
546 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','P','a','t','c','h','`',' ',
547 '(','`','F','i','l','e','_','`',',','`','S','e','q','u','e','n','c','e','`',',',
548 '`','P','a','t','c','h','S','i','z','e','`',',','`','A','t','t','r','i','b','u','t','e','s','`',',',
549 '`','H','e','a','d','e','r','`',',','`','S','t','r','e','a','m','R','e','f','_','`',')',' ',
550 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
551 struct patch_offset
*po
;
552 const WCHAR
*file
= MSI_RecordGetString( rec
, 1 );
553 UINT r
= ERROR_SUCCESS
, seq
= MSI_RecordGetInteger( rec
, 2 );
555 LIST_FOR_EACH_ENTRY( po
, &pos
->patches
, struct patch_offset
, entry
)
557 if (seq
== po
->sequence
&& !strcmpiW( file
, po
->name
))
559 MSIQUERY
*delete_view
, *insert_view
;
562 r
= MSI_DatabaseOpenViewW( db
, delete_query
, &delete_view
);
563 if (r
!= ERROR_SUCCESS
) return r
;
565 rec2
= MSI_CreateRecord( 2 );
566 MSI_RecordSetStringW( rec2
, 1, po
->name
);
567 MSI_RecordSetInteger( rec2
, 2, po
->sequence
);
568 r
= MSI_ViewExecute( delete_view
, rec2
);
569 msiobj_release( &delete_view
->hdr
);
570 msiobj_release( &rec2
->hdr
);
571 if (r
!= ERROR_SUCCESS
) return r
;
573 r
= MSI_DatabaseOpenViewW( db
, insert_query
, &insert_view
);
574 if (r
!= ERROR_SUCCESS
) return r
;
576 MSI_RecordSetInteger( rec
, 2, po
->sequence
+ pos
->offset_to_apply
);
578 r
= MSI_ViewExecute( insert_view
, rec
);
579 msiobj_release( &insert_view
->hdr
);
580 if (r
!= ERROR_SUCCESS
)
581 ERR("Failed to update offset for filepatch %s (%u)\n", debugstr_w(file
), r
);
588 static UINT
patch_offset_modify_db( MSIDATABASE
*db
, struct patch_offset_list
*pos
)
590 static const WCHAR file_query
[] = {
591 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','i','l','e','`',' ',
592 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>','=',' ','?',' ',
593 'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','<','=',' ','?',' ',
594 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
595 static const WCHAR patch_query
[] = {
596 'S','E','L','E','C','T',' ','*','F','R','O','M',' ','`','P','a','t','c','h','`',' ',
597 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>','=',' ','?',' ',
598 'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','<','=',' ','?',' ',
599 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
602 UINT r
, min
= pos
->min
, max
= pos
->max
, r_fetch
;
604 r
= MSI_DatabaseOpenViewW( db
, file_query
, &view
);
605 if (r
!= ERROR_SUCCESS
)
606 return ERROR_SUCCESS
;
608 rec
= MSI_CreateRecord( 2 );
609 MSI_RecordSetInteger( rec
, 1, min
);
610 MSI_RecordSetInteger( rec
, 2, max
);
612 r
= MSI_ViewExecute( view
, rec
);
613 msiobj_release( &rec
->hdr
);
614 if (r
!= ERROR_SUCCESS
)
617 while ((r_fetch
= MSI_ViewFetch( view
, &rec
)) == ERROR_SUCCESS
)
619 r
= patch_update_file_sequence( db
, pos
, view
, rec
);
620 msiobj_release( &rec
->hdr
);
621 if (r
!= ERROR_SUCCESS
) goto done
;
623 msiobj_release( &view
->hdr
);
625 r
= MSI_DatabaseOpenViewW( db
, patch_query
, &view
);
626 if (r
!= ERROR_SUCCESS
)
627 return ERROR_SUCCESS
;
629 rec
= MSI_CreateRecord( 2 );
630 MSI_RecordSetInteger( rec
, 1, min
);
631 MSI_RecordSetInteger( rec
, 2, max
);
633 r
= MSI_ViewExecute( view
, rec
);
634 msiobj_release( &rec
->hdr
);
635 if (r
!= ERROR_SUCCESS
)
638 while ((r_fetch
= MSI_ViewFetch( view
, &rec
)) == ERROR_SUCCESS
)
640 r
= patch_update_filepatch_sequence( db
, pos
, view
, rec
);
641 msiobj_release( &rec
->hdr
);
642 if (r
!= ERROR_SUCCESS
) goto done
;
646 msiobj_release( &view
->hdr
);
650 static const WCHAR patch_media_query
[] = {
651 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
652 'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
653 'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
654 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
667 static UINT
patch_add_media( MSIPACKAGE
*package
, IStorage
*storage
, MSIPATCHINFO
*patch
)
669 static const WCHAR delete_query
[] = {
670 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
671 'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0};
672 static const WCHAR insert_query
[] = {
673 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ',
674 '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',',
675 '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',',
676 '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ',
677 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
681 struct list media_list
;
682 struct patch_media
*media
, *next
;
684 r
= MSI_DatabaseOpenViewW( package
->db
, patch_media_query
, &view
);
685 if (r
!= ERROR_SUCCESS
) return r
;
687 r
= MSI_ViewExecute( view
, 0 );
688 if (r
!= ERROR_SUCCESS
)
690 msiobj_release( &view
->hdr
);
691 TRACE("query failed %u\n", r
);
694 list_init( &media_list
);
695 while (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
697 disk_id
= MSI_RecordGetInteger( rec
, 1 );
698 TRACE("disk_id %u\n", disk_id
);
699 if (disk_id
>= MSI_INITIAL_MEDIA_TRANSFORM_DISKID
)
701 msiobj_release( &rec
->hdr
);
704 if (!(media
= msi_alloc( sizeof( *media
)))) {
705 msiobj_release( &rec
->hdr
);
708 media
->disk_id
= disk_id
;
709 media
->last_sequence
= MSI_RecordGetInteger( rec
, 2 );
710 media
->prompt
= msi_dup_record_field( rec
, 3 );
711 media
->cabinet
= msi_dup_record_field( rec
, 4 );
712 media
->volume
= msi_dup_record_field( rec
, 5 );
713 media
->source
= msi_dup_record_field( rec
, 6 );
715 list_add_tail( &media_list
, &media
->entry
);
716 msiobj_release( &rec
->hdr
);
718 LIST_FOR_EACH_ENTRY( media
, &media_list
, struct patch_media
, entry
)
720 MSIQUERY
*delete_view
, *insert_view
;
722 r
= MSI_DatabaseOpenViewW( package
->db
, delete_query
, &delete_view
);
723 if (r
!= ERROR_SUCCESS
) goto done
;
725 rec
= MSI_CreateRecord( 1 );
726 MSI_RecordSetInteger( rec
, 1, media
->disk_id
);
728 r
= MSI_ViewExecute( delete_view
, rec
);
729 msiobj_release( &delete_view
->hdr
);
730 msiobj_release( &rec
->hdr
);
731 if (r
!= ERROR_SUCCESS
) goto done
;
733 r
= MSI_DatabaseOpenViewW( package
->db
, insert_query
, &insert_view
);
734 if (r
!= ERROR_SUCCESS
) goto done
;
736 disk_id
= package
->db
->media_transform_disk_id
;
737 TRACE("disk id %u\n", disk_id
);
738 TRACE("last sequence %u\n", media
->last_sequence
);
739 TRACE("prompt %s\n", debugstr_w(media
->prompt
));
740 TRACE("cabinet %s\n", debugstr_w(media
->cabinet
));
741 TRACE("volume %s\n", debugstr_w(media
->volume
));
742 TRACE("source %s\n", debugstr_w(media
->source
));
744 rec
= MSI_CreateRecord( 6 );
745 MSI_RecordSetInteger( rec
, 1, disk_id
);
746 MSI_RecordSetInteger( rec
, 2, media
->last_sequence
);
747 MSI_RecordSetStringW( rec
, 3, media
->prompt
);
748 MSI_RecordSetStringW( rec
, 4, media
->cabinet
);
749 MSI_RecordSetStringW( rec
, 5, media
->volume
);
750 MSI_RecordSetStringW( rec
, 6, media
->source
);
752 r
= MSI_ViewExecute( insert_view
, rec
);
753 msiobj_release( &insert_view
->hdr
);
754 msiobj_release( &rec
->hdr
);
755 if (r
!= ERROR_SUCCESS
) goto done
;
757 r
= msi_add_cabinet_stream( package
, disk_id
, storage
, media
->cabinet
);
758 if (r
!= ERROR_SUCCESS
) ERR("failed to add cabinet stream %u\n", r
);
761 patch
->disk_id
= disk_id
;
762 package
->db
->media_transform_disk_id
++;
767 msiobj_release( &view
->hdr
);
768 LIST_FOR_EACH_ENTRY_SAFE( media
, next
, &media_list
, struct patch_media
, entry
)
770 list_remove( &media
->entry
);
771 msi_free( media
->prompt
);
772 msi_free( media
->cabinet
);
773 msi_free( media
->volume
);
774 msi_free( media
->source
);
780 static UINT
patch_set_offsets( MSIDATABASE
*db
, MSIPATCHINFO
*patch
)
786 r
= MSI_DatabaseOpenViewW( db
, patch_media_query
, &view
);
787 if (r
!= ERROR_SUCCESS
)
790 r
= MSI_ViewExecute( view
, 0 );
791 if (r
!= ERROR_SUCCESS
)
794 while (MSI_ViewFetch( view
, &rec
) == ERROR_SUCCESS
)
796 UINT offset
, last_sequence
= MSI_RecordGetInteger( rec
, 2 );
797 struct patch_offset_list
*pos
;
799 /* FIXME: set/check Source field instead? */
800 if (last_sequence
>= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET
)
802 msiobj_release( &rec
->hdr
);
805 pos
= patch_offset_list_create();
806 patch_offset_get_files( db
, last_sequence
, pos
);
807 patch_offset_get_filepatches( db
, last_sequence
, pos
);
809 offset
= db
->media_transform_offset
- pos
->min
;
810 last_sequence
= offset
+ pos
->max
;
812 last_sequence
+= pos
->min
;
813 pos
->offset_to_apply
= offset
;
816 r
= patch_offset_modify_db( db
, pos
);
817 if (r
!= ERROR_SUCCESS
)
818 ERR("Failed to set offsets, expect breakage (%u)\n", r
);
820 MSI_RecordSetInteger( rec
, 2, last_sequence
);
821 r
= MSI_ViewModify( view
, MSIMODIFY_UPDATE
, rec
);
822 if (r
!= ERROR_SUCCESS
)
823 ERR("Failed to update Media table entry, expect breakage (%u)\n", r
);
825 db
->media_transform_offset
= last_sequence
+ 1;
827 patch_offset_list_free( pos
);
828 msiobj_release( &rec
->hdr
);
832 msiobj_release( &view
->hdr
);
836 static UINT
msi_apply_patch_db( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
, MSIPATCHINFO
*patch
)
838 UINT i
, r
= ERROR_SUCCESS
;
841 /* apply substorage transforms */
842 substorage
= msi_split_string( patch
->transforms
, ';' );
843 for (i
= 0; substorage
&& substorage
[i
] && r
== ERROR_SUCCESS
; i
++)
845 r
= apply_substorage_transform( package
, patch_db
, substorage
[i
] );
846 if (r
== ERROR_SUCCESS
)
848 r
= patch_set_offsets( package
->db
, patch
);
849 if (r
== ERROR_SUCCESS
)
850 r
= patch_add_media( package
, patch_db
->storage
, patch
);
853 msi_free( substorage
);
854 if (r
!= ERROR_SUCCESS
)
857 r
= patch_set_media_source_prop( package
);
858 if (r
!= ERROR_SUCCESS
)
861 patch
->state
= MSIPATCHSTATE_APPLIED
;
862 list_add_tail( &package
->patches
, &patch
->entry
);
863 return ERROR_SUCCESS
;
866 void msi_free_patchinfo( MSIPATCHINFO
*patch
)
868 msi_free( patch
->patchcode
);
869 msi_free( patch
->products
);
870 msi_free( patch
->transforms
);
871 msi_free( patch
->filename
);
872 msi_free( patch
->localfile
);
876 static UINT
msi_apply_patch_package( MSIPACKAGE
*package
, const WCHAR
*file
)
878 static const WCHAR dotmsp
[] = {'.','m','s','p',0};
879 MSIDATABASE
*patch_db
= NULL
;
880 WCHAR localfile
[MAX_PATH
];
882 MSIPATCHINFO
*patch
= NULL
;
885 TRACE("%p, %s\n", package
, debugstr_w(file
));
887 r
= MSI_OpenDatabaseW( file
, MSIDBOPEN_READONLY
+ MSIDBOPEN_PATCHFILE
, &patch_db
);
888 if (r
!= ERROR_SUCCESS
)
890 ERR("failed to open patch collection %s\n", debugstr_w( file
) );
893 r
= msi_get_suminfo( patch_db
->storage
, 0, &si
);
894 if (r
!= ERROR_SUCCESS
)
896 msiobj_release( &patch_db
->hdr
);
899 r
= msi_check_patch_applicable( package
, si
);
900 if (r
!= ERROR_SUCCESS
)
902 TRACE("patch not applicable\n");
906 r
= msi_parse_patch_summary( si
, &patch
);
907 if ( r
!= ERROR_SUCCESS
)
910 r
= msi_create_empty_local_file( localfile
, dotmsp
);
911 if ( r
!= ERROR_SUCCESS
)
914 r
= ERROR_OUTOFMEMORY
;
915 patch
->registered
= FALSE
;
916 if (!(patch
->filename
= strdupW( file
))) goto done
;
917 if (!(patch
->localfile
= strdupW( localfile
))) goto done
;
919 r
= msi_apply_patch_db( package
, patch_db
, patch
);
920 if (r
!= ERROR_SUCCESS
) WARN("patch failed to apply %u\n", r
);
923 msiobj_release( &si
->hdr
);
924 msiobj_release( &patch_db
->hdr
);
925 if (patch
&& r
!= ERROR_SUCCESS
)
927 DeleteFileW( patch
->localfile
);
928 msi_free_patchinfo( patch
);
933 /* get the PATCH property, and apply all the patches it specifies */
934 UINT
msi_apply_patches( MSIPACKAGE
*package
)
936 LPWSTR patch_list
, *patches
;
937 UINT i
, r
= ERROR_SUCCESS
;
939 patch_list
= msi_dup_property( package
->db
, szPatch
);
941 TRACE("patches to be applied: %s\n", debugstr_w(patch_list
));
943 patches
= msi_split_string( patch_list
, ';' );
944 for (i
= 0; patches
&& patches
[i
] && r
== ERROR_SUCCESS
; i
++)
945 r
= msi_apply_patch_package( package
, patches
[i
] );
948 msi_free( patch_list
);
952 UINT
msi_apply_transforms( MSIPACKAGE
*package
)
954 static const WCHAR szTransforms
[] = {'T','R','A','N','S','F','O','R','M','S',0};
955 LPWSTR xform_list
, *xforms
;
956 UINT i
, r
= ERROR_SUCCESS
;
958 xform_list
= msi_dup_property( package
->db
, szTransforms
);
959 xforms
= msi_split_string( xform_list
, ';' );
961 for (i
= 0; xforms
&& xforms
[i
] && r
== ERROR_SUCCESS
; i
++)
963 if (xforms
[i
][0] == ':')
964 r
= apply_substorage_transform( package
, package
->db
, xforms
[i
] );
969 if (!PathIsRelativeW( xforms
[i
] )) transform
= xforms
[i
];
972 WCHAR
*p
= strrchrW( package
->PackagePath
, '\\' );
973 DWORD len
= p
- package
->PackagePath
+ 1;
975 if (!(transform
= msi_alloc( (len
+ strlenW( xforms
[i
] ) + 1) * sizeof(WCHAR
)) ))
978 msi_free( xform_list
);
979 return ERROR_OUTOFMEMORY
;
981 memcpy( transform
, package
->PackagePath
, len
* sizeof(WCHAR
) );
982 memcpy( transform
+ len
, xforms
[i
], (strlenW( xforms
[i
] ) + 1) * sizeof(WCHAR
) );
984 r
= MSI_DatabaseApplyTransformW( package
->db
, transform
, 0 );
985 if (transform
!= xforms
[i
]) msi_free( transform
);
989 msi_free( xform_list
);
993 UINT
msi_apply_registered_patch( MSIPACKAGE
*package
, LPCWSTR patch_code
)
997 WCHAR patch_file
[MAX_PATH
];
998 MSIDATABASE
*patch_db
;
999 MSIPATCHINFO
*patch_info
;
1002 TRACE("%p, %s\n", package
, debugstr_w(patch_code
));
1004 len
= sizeof(patch_file
) / sizeof(WCHAR
);
1005 r
= MsiGetPatchInfoExW( patch_code
, package
->ProductCode
, NULL
, package
->Context
,
1006 INSTALLPROPERTY_LOCALPACKAGEW
, patch_file
, &len
);
1007 if (r
!= ERROR_SUCCESS
)
1009 ERR("failed to get patch filename %u\n", r
);
1012 r
= MSI_OpenDatabaseW( patch_file
, MSIDBOPEN_READONLY
+ MSIDBOPEN_PATCHFILE
, &patch_db
);
1013 if (r
!= ERROR_SUCCESS
)
1015 ERR("failed to open patch database %s\n", debugstr_w( patch_file
));
1018 r
= msi_get_suminfo( patch_db
->storage
, 0, &si
);
1019 if (r
!= ERROR_SUCCESS
)
1021 msiobj_release( &patch_db
->hdr
);
1024 r
= msi_parse_patch_summary( si
, &patch_info
);
1025 msiobj_release( &si
->hdr
);
1026 if (r
!= ERROR_SUCCESS
)
1028 ERR("failed to parse patch summary %u\n", r
);
1029 msiobj_release( &patch_db
->hdr
);
1032 patch_info
->registered
= TRUE
;
1033 patch_info
->localfile
= strdupW( patch_file
);
1034 if (!patch_info
->localfile
)
1036 msiobj_release( &patch_db
->hdr
);
1037 msi_free_patchinfo( patch_info
);
1038 return ERROR_OUTOFMEMORY
;
1040 r
= msi_apply_patch_db( package
, patch_db
, patch_info
);
1041 msiobj_release( &patch_db
->hdr
);
1042 if (r
!= ERROR_SUCCESS
)
1044 ERR("failed to apply patch %u\n", r
);
1045 msi_free_patchinfo( patch_info
);