dwrite: Implement CreateFontFaceFromHdc().
[wine.git] / dlls / msi / patch.c
bloba3a8d3215edfc0a49ca7dace817bd34c4e8054e0
1 /*
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
22 #include <stdarg.h>
23 #define COBJMACROS
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winreg.h"
27 #include "objbase.h"
28 #include "shlwapi.h"
29 #include "wine/debug.h"
30 #include "wine/unicode.h"
31 #include "msipriv.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(msi);
35 static BOOL match_language( MSIPACKAGE *package, LANGID langid )
37 UINT i;
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;
44 return FALSE;
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)
74 WCHAR *template;
75 const WCHAR *p;
77 if (!si)
79 ERR("no summary information!\n");
80 goto end;
82 if (!(template = msi_suminfo_dup_string( si, PID_TEMPLATE )))
84 ERR("no template property!\n");
85 goto end;
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;
92 msi_free( template );
95 end:
96 msiobj_release( &si->hdr );
97 if (valid_flags & ~wanted_flags) return ERROR_FUNCTION_FAILED;
98 TRACE("applicable transform\n");
99 return ERROR_SUCCESS;
102 static UINT apply_substorage_transform( MSIPACKAGE *package, MSIDATABASE *patch_db, LPCWSTR name )
104 UINT ret = ERROR_FUNCTION_FAILED;
105 IStorage *stg = NULL;
106 HRESULT r;
108 TRACE("%p %s\n", package, debugstr_w(name));
110 if (*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 );
116 if (SUCCEEDED(r))
118 ret = check_transform_applicable( package, stg );
119 if (ret == ERROR_SUCCESS)
120 msi_table_apply_transform( package->db, stg );
121 else
122 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
123 IStorage_Release( stg );
125 else
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 );
138 if (!product_code)
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;
150 msi_free( guids );
151 msi_free( guid_list );
152 msi_free( product_code );
153 return ret;
156 static UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
158 MSIPATCHINFO *pi;
159 UINT r = ERROR_SUCCESS;
160 WCHAR *p;
162 if (!(pi = msi_alloc_zero( sizeof(MSIPATCHINFO) )))
164 return ERROR_OUTOFMEMORY;
166 if (!(pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER )))
168 msi_free( pi );
169 return ERROR_OUTOFMEMORY;
171 p = pi->patchcode;
172 if (*p != '{')
174 msi_free( pi->patchcode );
175 msi_free( pi );
176 return ERROR_PATCH_PACKAGE_INVALID;
178 if (!(p = strchrW( p + 1, '}' )))
180 msi_free( pi->patchcode );
181 msi_free( pi );
182 return ERROR_PATCH_PACKAGE_INVALID;
184 if (p[1])
186 FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
187 p[1] = 0;
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 );
193 msi_free( pi );
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 );
200 msi_free( pi );
201 return ERROR_OUTOFMEMORY;
203 *patch = pi;
204 return r;
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};
213 MSIQUERY *view;
214 MSIRECORD *rec;
215 const WCHAR *property;
216 WCHAR *patch;
217 UINT r;
219 r = MSI_DatabaseOpenViewW( package->db, query, &view );
220 if (r != ERROR_SUCCESS)
221 return r;
223 r = MSI_ViewExecute( view, 0 );
224 if (r != ERROR_SUCCESS)
225 goto done;
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 );
232 msi_free( patch );
233 msiobj_release( &rec->hdr );
236 done:
237 msiobj_release( &view->hdr );
238 return r;
241 struct patch_offset
243 struct list entry;
244 WCHAR *name;
245 UINT sequence;
248 struct patch_offset_list
250 struct list files;
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;
260 pos->min = 999999;
261 return pos;
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 );
271 msi_free( po );
273 msi_free( pos );
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};
282 MSIQUERY *view;
283 MSIRECORD *rec;
284 UINT r;
286 r = MSI_DatabaseOpenViewW( db, query, &view );
287 if (r != ERROR_SUCCESS)
288 return;
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)
296 return;
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 );
305 pos->count++;
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};
317 MSIQUERY *view;
318 MSIRECORD *rec;
319 UINT r;
321 r = MSI_DatabaseOpenViewW( db, query, &view );
322 if (r != ERROR_SUCCESS)
323 return;
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)
331 return;
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 );
345 pos->count++;
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;
360 MSIRECORD *rec;
361 MSIQUERY *view;
362 UINT r;
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)
375 goto done;
377 LIST_FOR_EACH_ENTRY( po, &pos->files, struct patch_offset, entry )
379 UINT r_fetch;
380 while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
382 const WCHAR *file = MSI_RecordGetString( rec, 1 );
383 UINT seq;
385 if (!strcmpiW( file, po->name ))
387 /* update record */
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 );
394 break;
396 msiobj_release( &rec->hdr );
398 if (r_fetch != ERROR_SUCCESS) break;
401 done:
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};
412 struct patch_media
414 struct list entry;
415 UINT disk_id;
416 UINT last_sequence;
417 WCHAR *prompt;
418 WCHAR *cabinet;
419 WCHAR *volume;
420 WCHAR *source;
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};
434 MSIQUERY *view;
435 MSIRECORD *rec;
436 UINT r, disk_id;
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);
448 return 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 );
458 continue;
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++;
515 done:
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 );
524 msi_free( media );
526 return r;
529 static UINT set_patch_offsets( MSIDATABASE *db )
531 MSIQUERY *view;
532 MSIRECORD *rec;
533 UINT r;
535 r = MSI_DatabaseOpenViewW( db, patch_media_query, &view );
536 if (r != ERROR_SUCCESS)
537 return r;
539 r = MSI_ViewExecute( view, 0 );
540 if (r != ERROR_SUCCESS)
541 goto done;
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 );
552 continue;
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;
564 if (pos->count)
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 );
577 done:
578 msiobj_release( &view->hdr );
579 return r;
582 static UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
584 UINT i, r = ERROR_SUCCESS;
585 WCHAR **substorage;
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)
600 return r;
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 );
616 msi_free( patch );
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];
624 MSISUMMARYINFO *si;
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 ) );
634 return r;
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");
645 r = ERROR_SUCCESS;
646 goto done;
648 r = msi_parse_patch_summary( si, &patch );
649 if ( r != ERROR_SUCCESS )
650 goto done;
652 r = msi_create_empty_local_file( localfile, dotmsp );
653 if ( r != ERROR_SUCCESS )
654 goto done;
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);
663 done:
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 );
671 return r;
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] );
688 msi_free( patches );
689 msi_free( patch_list );
690 return r;
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] );
706 else
708 WCHAR *transform;
710 if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
711 else
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)) ))
718 msi_free( xforms );
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 );
729 msi_free( xforms );
730 msi_free( xform_list );
731 return r;
734 UINT msi_apply_registered_patch( MSIPACKAGE *package, LPCWSTR patch_code )
736 UINT r;
737 DWORD len;
738 WCHAR patch_file[MAX_PATH];
739 MSIDATABASE *patch_db;
740 MSIPATCHINFO *patch_info;
741 MSISUMMARYINFO *si;
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);
749 return 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 ));
755 return r;
757 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
758 if (!si)
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 );
769 return r;
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 );
785 return r;