msi: Don't create a temporary copy of the package.
[wine/multimedia.git] / dlls / msi / patch.c
blobc2660adf088d092644fe226b74dd7b5b42c1415f
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 *patch )
49 LPWSTR prod_code, patch_product, template = NULL;
50 UINT ret = ERROR_FUNCTION_FAILED;
52 prod_code = msi_dup_property( package->db, szProductCode );
53 patch_product = msi_get_suminfo_product( patch );
55 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
57 if (strstrW( patch_product, prod_code ))
59 MSISUMMARYINFO *si;
60 const WCHAR *p;
62 si = MSI_GetSummaryInformationW( patch, 0 );
63 if (!si)
65 ERR("no summary information!\n");
66 goto end;
68 template = msi_suminfo_dup_string( si, PID_TEMPLATE );
69 if (!template)
71 ERR("no template property!\n");
72 msiobj_release( &si->hdr );
73 goto end;
75 if (!template[0])
77 ret = ERROR_SUCCESS;
78 msiobj_release( &si->hdr );
79 goto end;
81 TRACE("template: %s\n", debugstr_w(template));
82 p = strchrW( template, ';' );
83 if (p && match_language( package, atoiW( p + 1 ) ))
85 TRACE("applicable transform\n");
86 ret = ERROR_SUCCESS;
88 /* FIXME: check platform */
89 msiobj_release( &si->hdr );
92 end:
93 msi_free( patch_product );
94 msi_free( prod_code );
95 msi_free( template );
96 return ret;
99 static UINT apply_substorage_transform( MSIPACKAGE *package, MSIDATABASE *patch_db, LPCWSTR name )
101 UINT ret = ERROR_FUNCTION_FAILED;
102 IStorage *stg = NULL;
103 HRESULT r;
105 TRACE("%p %s\n", package, debugstr_w(name));
107 if (*name++ != ':')
109 ERR("expected a colon in %s\n", debugstr_w(name));
110 return ERROR_FUNCTION_FAILED;
112 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
113 if (SUCCEEDED(r))
115 ret = check_transform_applicable( package, stg );
116 if (ret == ERROR_SUCCESS)
117 msi_table_apply_transform( package->db, stg );
118 else
119 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
120 IStorage_Release( stg );
122 else
124 ERR("failed to open substorage %s\n", debugstr_w(name));
126 return ERROR_SUCCESS;
129 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
131 LPWSTR guid_list, *guids, product_code;
132 UINT i, ret = ERROR_FUNCTION_FAILED;
134 product_code = msi_dup_property( package->db, szProductCode );
135 if (!product_code)
137 /* FIXME: the property ProductCode should be written into the DB somewhere */
138 ERR("no product code to check\n");
139 return ERROR_SUCCESS;
141 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
142 guids = msi_split_string( guid_list, ';' );
143 for (i = 0; guids[i] && ret != ERROR_SUCCESS; i++)
145 if (!strcmpW( guids[i], product_code )) ret = ERROR_SUCCESS;
147 msi_free( guids );
148 msi_free( guid_list );
149 msi_free( product_code );
150 return ret;
153 static UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
155 MSIPATCHINFO *pi;
156 UINT r = ERROR_SUCCESS;
157 WCHAR *p;
159 if (!(pi = msi_alloc_zero( sizeof(MSIPATCHINFO) )))
161 return ERROR_OUTOFMEMORY;
163 if (!(pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER )))
165 msi_free( pi );
166 return ERROR_OUTOFMEMORY;
168 p = pi->patchcode;
169 if (*p != '{')
171 msi_free( pi->patchcode );
172 msi_free( pi );
173 return ERROR_PATCH_PACKAGE_INVALID;
175 if (!(p = strchrW( p + 1, '}' )))
177 msi_free( pi->patchcode );
178 msi_free( pi );
179 return ERROR_PATCH_PACKAGE_INVALID;
181 if (p[1])
183 FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
184 p[1] = 0;
186 TRACE("patch code %s\n", debugstr_w(pi->patchcode));
188 if (!(pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR )))
190 msi_free( pi->patchcode );
191 msi_free( pi );
192 return ERROR_OUTOFMEMORY;
194 *patch = pi;
195 return r;
198 static UINT patch_set_media_source_prop( MSIPACKAGE *package )
200 static const WCHAR query[] = {
201 'S','E','L','E','C','T',' ','`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
202 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ',
203 'I','S',' ','N','O','T',' ','N','U','L','L',0};
204 MSIQUERY *view;
205 MSIRECORD *rec;
206 const WCHAR *property;
207 WCHAR *patch;
208 UINT r;
210 r = MSI_DatabaseOpenViewW( package->db, query, &view );
211 if (r != ERROR_SUCCESS)
212 return r;
214 r = MSI_ViewExecute( view, 0 );
215 if (r != ERROR_SUCCESS)
216 goto done;
218 if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
220 property = MSI_RecordGetString( rec, 1 );
221 patch = msi_dup_property( package->db, szPatch );
222 msi_set_property( package->db, property, patch );
223 msi_free( patch );
224 msiobj_release( &rec->hdr );
227 done:
228 msiobj_release( &view->hdr );
229 return r;
232 struct patch_offset
234 struct list entry;
235 WCHAR *name;
236 UINT sequence;
239 struct patch_offset_list
241 struct list files;
242 UINT count, min, max;
243 UINT offset_to_apply;
246 static struct patch_offset_list *patch_offset_list_create( void )
248 struct patch_offset_list *pos = msi_alloc( sizeof(struct patch_offset_list) );
249 list_init( &pos->files );
250 pos->count = pos->max = 0;
251 pos->min = 999999;
252 return pos;
255 static void patch_offset_list_free( struct patch_offset_list *pos )
257 struct patch_offset *po, *po2;
259 LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct patch_offset, entry )
261 msi_free( po->name );
262 msi_free( po );
264 msi_free( pos );
267 static void patch_offset_get_patches( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
269 static const WCHAR query[] = {
270 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
271 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
272 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
273 MSIQUERY *view;
274 MSIRECORD *rec;
275 UINT r;
277 r = MSI_DatabaseOpenViewW( db, query, &view );
278 if (r != ERROR_SUCCESS)
279 return;
281 rec = MSI_CreateRecord( 1 );
282 MSI_RecordSetInteger( rec, 1, last_sequence );
284 r = MSI_ViewExecute( view, rec );
285 msiobj_release( &rec->hdr );
286 if (r != ERROR_SUCCESS)
287 return;
289 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
291 UINT sequence = MSI_RecordGetInteger( rec, 2 );
293 /* FIXME: we only use the max/min sequence numbers for now */
294 pos->min = min( pos->min, sequence );
295 pos->max = max( pos->max, sequence );
296 pos->count++;
297 msiobj_release( &rec->hdr );
299 msiobj_release( &view->hdr );
302 static void patch_offset_get_files( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
304 static const WCHAR query[] = {
305 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
306 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
307 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
308 MSIQUERY *view;
309 MSIRECORD *rec;
310 UINT r;
312 r = MSI_DatabaseOpenViewW( db, query, &view );
313 if (r != ERROR_SUCCESS)
314 return;
316 rec = MSI_CreateRecord( 1 );
317 MSI_RecordSetInteger( rec, 1, last_sequence );
319 r = MSI_ViewExecute( view, rec );
320 msiobj_release( &rec->hdr );
321 if (r != ERROR_SUCCESS)
322 return;
324 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
326 UINT attributes = MSI_RecordGetInteger( rec, 7 );
327 if (attributes & msidbFileAttributesPatchAdded)
329 struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) );
331 po->name = msi_dup_record_field( rec, 1 );
332 po->sequence = MSI_RecordGetInteger( rec, 8 );
333 pos->min = min( pos->min, po->sequence );
334 pos->max = max( pos->max, po->sequence );
335 list_add_tail( &pos->files, &po->entry );
336 pos->count++;
338 msiobj_release( &rec->hdr );
340 msiobj_release( &view->hdr );
343 static UINT patch_offset_modify_db( MSIDATABASE *db, struct patch_offset_list *pos )
345 static const WCHAR query[] = {
346 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
347 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','>','=',' ','?',' ',
348 'A','N','D',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
349 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
350 struct patch_offset *po;
351 MSIRECORD *rec;
352 MSIQUERY *view;
353 UINT r;
355 r = MSI_DatabaseOpenViewW( db, query, &view );
356 if (r != ERROR_SUCCESS)
357 return ERROR_SUCCESS;
359 rec = MSI_CreateRecord( 2 );
360 MSI_RecordSetInteger( rec, 1, pos->min );
361 MSI_RecordSetInteger( rec, 2, pos->max );
363 r = MSI_ViewExecute( view, rec );
364 msiobj_release( &rec->hdr );
365 if (r != ERROR_SUCCESS)
366 goto done;
368 LIST_FOR_EACH_ENTRY( po, &pos->files, struct patch_offset, entry )
370 UINT r_fetch;
371 while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
373 const WCHAR *file = MSI_RecordGetString( rec, 1 );
374 UINT seq;
376 if (!strcmpiW( file, po->name ))
378 /* update record */
379 seq = MSI_RecordGetInteger( rec, 8 );
380 MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply );
381 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
382 if (r != ERROR_SUCCESS)
383 ERR("Failed to update offset for file %s\n", debugstr_w(file));
384 msiobj_release( &rec->hdr );
385 break;
387 msiobj_release( &rec->hdr );
389 if (r_fetch != ERROR_SUCCESS) break;
392 done:
393 msiobj_release( &view->hdr );
394 return ERROR_SUCCESS;
397 static const WCHAR patch_media_query[] = {
398 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
399 'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
400 'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
401 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
403 struct patch_media
405 struct list entry;
406 UINT disk_id;
407 UINT last_sequence;
408 WCHAR *prompt;
409 WCHAR *cabinet;
410 WCHAR *volume;
411 WCHAR *source;
414 static UINT add_patch_media( MSIPACKAGE *package, IStorage *patch )
416 static const WCHAR delete_query[] = {
417 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
418 'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0};
419 static const WCHAR insert_query[] = {
420 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ',
421 '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',',
422 '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',',
423 '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ',
424 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
425 MSIQUERY *view;
426 MSIRECORD *rec;
427 UINT r, disk_id;
428 struct list media_list;
429 struct patch_media *media, *next;
431 r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view );
432 if (r != ERROR_SUCCESS) return r;
434 r = MSI_ViewExecute( view, 0 );
435 if (r != ERROR_SUCCESS)
437 msiobj_release( &view->hdr );
438 TRACE("query failed %u\n", r);
439 return r;
441 list_init( &media_list );
442 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
444 disk_id = MSI_RecordGetInteger( rec, 1 );
445 TRACE("disk_id %u\n", disk_id);
446 if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
448 msiobj_release( &rec->hdr );
449 continue;
451 if (!(media = msi_alloc( sizeof( *media )))) goto done;
452 media->disk_id = disk_id;
453 media->last_sequence = MSI_RecordGetInteger( rec, 2 );
454 media->prompt = msi_dup_record_field( rec, 3 );
455 media->cabinet = msi_dup_record_field( rec, 4 );
456 media->volume = msi_dup_record_field( rec, 5 );
457 media->source = msi_dup_record_field( rec, 6 );
459 list_add_tail( &media_list, &media->entry );
460 msiobj_release( &rec->hdr );
462 LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry )
464 MSIQUERY *delete_view, *insert_view;
466 r = MSI_DatabaseOpenViewW( package->db, delete_query, &delete_view );
467 if (r != ERROR_SUCCESS) goto done;
469 rec = MSI_CreateRecord( 1 );
470 MSI_RecordSetInteger( rec, 1, media->disk_id );
472 r = MSI_ViewExecute( delete_view, rec );
473 msiobj_release( &delete_view->hdr );
474 msiobj_release( &rec->hdr );
475 if (r != ERROR_SUCCESS) goto done;
477 r = MSI_DatabaseOpenViewW( package->db, insert_query, &insert_view );
478 if (r != ERROR_SUCCESS) goto done;
480 disk_id = package->db->media_transform_disk_id;
481 TRACE("disk id %u\n", disk_id);
482 TRACE("last sequence %u\n", media->last_sequence);
483 TRACE("prompt %s\n", debugstr_w(media->prompt));
484 TRACE("cabinet %s\n", debugstr_w(media->cabinet));
485 TRACE("volume %s\n", debugstr_w(media->volume));
486 TRACE("source %s\n", debugstr_w(media->source));
488 rec = MSI_CreateRecord( 6 );
489 MSI_RecordSetInteger( rec, 1, disk_id );
490 MSI_RecordSetInteger( rec, 2, media->last_sequence );
491 MSI_RecordSetStringW( rec, 3, media->prompt );
492 MSI_RecordSetStringW( rec, 4, media->cabinet );
493 MSI_RecordSetStringW( rec, 5, media->volume );
494 MSI_RecordSetStringW( rec, 6, media->source );
496 r = MSI_ViewExecute( insert_view, rec );
497 msiobj_release( &insert_view->hdr );
498 msiobj_release( &rec->hdr );
499 if (r != ERROR_SUCCESS) goto done;
501 r = msi_add_cabinet_stream( package, disk_id, patch, media->cabinet );
502 if (r != ERROR_SUCCESS) WARN("failed to add cabinet stream %u\n", r);
503 package->db->media_transform_disk_id++;
506 done:
507 msiobj_release( &view->hdr );
508 LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry )
510 list_remove( &media->entry );
511 msi_free( media->prompt );
512 msi_free( media->cabinet );
513 msi_free( media->volume );
514 msi_free( media->source );
515 msi_free( media );
517 return r;
520 static UINT set_patch_offsets( MSIDATABASE *db )
522 MSIQUERY *view;
523 MSIRECORD *rec;
524 UINT r;
526 r = MSI_DatabaseOpenViewW( db, patch_media_query, &view );
527 if (r != ERROR_SUCCESS)
528 return r;
530 r = MSI_ViewExecute( view, 0 );
531 if (r != ERROR_SUCCESS)
532 goto done;
534 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
536 UINT last_sequence = MSI_RecordGetInteger( rec, 2 );
537 struct patch_offset_list *pos;
539 /* FIXME: set/check Source field instead? */
540 if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
542 msiobj_release( &rec->hdr );
543 continue;
545 pos = patch_offset_list_create();
546 patch_offset_get_files( db, last_sequence, pos );
547 patch_offset_get_patches( db, last_sequence, pos );
548 if (pos->count)
550 UINT offset = db->media_transform_offset - pos->min;
551 last_sequence = offset + pos->max;
553 /* FIXME: this is for the patch table, which is not yet properly transformed */
554 last_sequence += pos->min;
555 pos->offset_to_apply = offset;
556 patch_offset_modify_db( db, pos );
558 MSI_RecordSetInteger( rec, 2, last_sequence );
559 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
560 if (r != ERROR_SUCCESS)
561 ERR("Failed to update Media table entry, expect breakage (%u)\n", r);
562 db->media_transform_offset = last_sequence + 1;
564 patch_offset_list_free( pos );
565 msiobj_release( &rec->hdr );
568 done:
569 msiobj_release( &view->hdr );
570 return r;
573 static UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
575 UINT i, r = ERROR_SUCCESS;
576 WCHAR **substorage;
578 /* apply substorage transforms */
579 substorage = msi_split_string( patch->transforms, ';' );
580 for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
582 r = apply_substorage_transform( package, patch_db, substorage[i] );
583 if (r == ERROR_SUCCESS)
585 add_patch_media( package, patch_db->storage );
586 set_patch_offsets( package->db );
589 msi_free( substorage );
590 if (r != ERROR_SUCCESS)
591 return r;
593 patch_set_media_source_prop( package );
595 patch->state = MSIPATCHSTATE_APPLIED;
596 list_add_tail( &package->patches, &patch->entry );
597 return ERROR_SUCCESS;
600 static UINT msi_apply_patch_package( MSIPACKAGE *package, const WCHAR *file )
602 static const WCHAR dotmsp[] = {'.','m','s','p',0};
603 MSIDATABASE *patch_db = NULL;
604 WCHAR localfile[MAX_PATH];
605 MSISUMMARYINFO *si;
606 MSIPATCHINFO *patch = NULL;
607 UINT r = ERROR_SUCCESS;
609 TRACE("%p %s\n", package, debugstr_w(file));
611 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
612 if (r != ERROR_SUCCESS)
614 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
615 return r;
617 if (!(si = MSI_GetSummaryInformationW( patch_db->storage, 0 )))
619 msiobj_release( &patch_db->hdr );
620 return ERROR_FUNCTION_FAILED;
622 r = msi_check_patch_applicable( package, si );
623 if (r != ERROR_SUCCESS)
625 TRACE("patch not applicable\n");
626 r = ERROR_SUCCESS;
627 goto done;
629 r = msi_parse_patch_summary( si, &patch );
630 if ( r != ERROR_SUCCESS )
631 goto done;
633 r = msi_create_empty_local_file( localfile, dotmsp );
634 if ( r != ERROR_SUCCESS )
635 goto done;
637 r = ERROR_OUTOFMEMORY;
638 if (!(patch->filename = strdupW( file ))) goto done;
639 if (!(patch->localfile = strdupW( localfile ))) goto done;
641 r = msi_apply_patch_db( package, patch_db, patch );
642 if (r != ERROR_SUCCESS) WARN("patch failed to apply %u\n", r);
644 done:
645 msiobj_release( &si->hdr );
646 msiobj_release( &patch_db->hdr );
647 if (patch && r != ERROR_SUCCESS)
649 msi_free( patch->patchcode );
650 msi_free( patch->transforms );
651 msi_free( patch->filename );
652 msi_free( patch->localfile );
653 msi_free( patch );
655 return r;
658 /* get the PATCH property, and apply all the patches it specifies */
659 UINT msi_apply_patches( MSIPACKAGE *package )
661 LPWSTR patch_list, *patches;
662 UINT i, r = ERROR_SUCCESS;
664 patch_list = msi_dup_property( package->db, szPatch );
666 TRACE("patches to be applied: %s\n", debugstr_w(patch_list));
668 patches = msi_split_string( patch_list, ';' );
669 for (i = 0; patches && patches[i] && r == ERROR_SUCCESS; i++)
670 r = msi_apply_patch_package( package, patches[i] );
672 msi_free( patches );
673 msi_free( patch_list );
674 return r;
677 UINT msi_apply_transforms( MSIPACKAGE *package )
679 static const WCHAR szTransforms[] = {'T','R','A','N','S','F','O','R','M','S',0};
680 LPWSTR xform_list, *xforms;
681 UINT i, r = ERROR_SUCCESS;
683 xform_list = msi_dup_property( package->db, szTransforms );
684 xforms = msi_split_string( xform_list, ';' );
686 for (i = 0; xforms && xforms[i] && r == ERROR_SUCCESS; i++)
688 if (xforms[i][0] == ':')
689 r = apply_substorage_transform( package, package->db, xforms[i] );
690 else
692 WCHAR *transform;
694 if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
695 else
697 WCHAR *p = strrchrW( package->PackagePath, '\\' );
698 DWORD len = p - package->PackagePath + 1;
700 if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
702 msi_free( xforms );
703 msi_free( xform_list );
704 return ERROR_OUTOFMEMORY;
706 memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
707 memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
709 r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
710 if (transform != xforms[i]) msi_free( transform );
713 msi_free( xforms );
714 msi_free( xform_list );
715 return r;
718 UINT msi_apply_registered_patch( MSIPACKAGE *package, LPCWSTR patch_code )
720 UINT r;
721 DWORD len;
722 WCHAR patch_file[MAX_PATH];
723 MSIDATABASE *patch_db;
724 MSIPATCHINFO *patch_info;
725 MSISUMMARYINFO *si;
727 len = sizeof(patch_file) / sizeof(WCHAR);
728 r = MsiGetPatchInfoExW( patch_code, package->ProductCode, NULL, package->Context,
729 INSTALLPROPERTY_LOCALPACKAGEW, patch_file, &len );
730 if (r != ERROR_SUCCESS)
732 ERR("failed to get patch filename %u\n", r);
733 return r;
735 r = MSI_OpenDatabaseW( patch_file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
736 if (r != ERROR_SUCCESS)
738 ERR("failed to open patch database %s\n", debugstr_w( patch_file ));
739 return r;
741 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
742 if (!si)
744 msiobj_release( &patch_db->hdr );
745 return ERROR_FUNCTION_FAILED;
747 r = msi_parse_patch_summary( si, &patch_info );
748 msiobj_release( &si->hdr );
749 if (r != ERROR_SUCCESS)
751 ERR("failed to parse patch summary %u\n", r);
752 msiobj_release( &patch_db->hdr );
753 return r;
755 patch_info->localfile = strdupW( patch_file );
756 if (!patch_info->localfile)
758 msiobj_release( &patch_db->hdr );
759 return ERROR_OUTOFMEMORY;
761 r = msi_apply_patch_db( package, patch_db, patch_info );
762 msiobj_release( &patch_db->hdr );
763 if (r != ERROR_SUCCESS)
765 ERR("failed to apply patch %u\n", r);
766 msi_free( patch_info->patchcode );
767 msi_free( patch_info->transforms );
768 msi_free( patch_info->localfile );
769 msi_free( patch_info );
771 return r;