msi/tests: Use a package code that is different from the product code.
[wine] / dlls / msi / patch.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2004,2005 Aric Stewart for CodeWeavers
5  * Copyright 2011 Hans Leidekker for CodeWeavers
6  *
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.
11  *
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.
16  *
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
20  */
21
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"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(msi);
34
35 static BOOL match_language( MSIPACKAGE *package, LANGID langid )
36 {
37     UINT i;
38
39     if (!package->num_langids || !langid) return TRUE;
40     for (i = 0; i < package->num_langids; i++)
41     {
42         if (package->langids[i] == langid) return TRUE;
43     }
44     return FALSE;
45 }
46
47 static UINT check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
48 {
49     LPWSTR prod_code, patch_product, template = NULL;
50     UINT ret = ERROR_FUNCTION_FAILED;
51
52     prod_code = msi_dup_property( package->db, szProductCode );
53     patch_product = msi_get_suminfo_product( patch );
54
55     TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
56
57     if (strstrW( patch_product, prod_code ))
58     {
59         MSISUMMARYINFO *si;
60         const WCHAR *p;
61
62         si = MSI_GetSummaryInformationW( patch, 0 );
63         if (!si)
64         {
65             ERR("no summary information!\n");
66             goto end;
67         }
68         template = msi_suminfo_dup_string( si, PID_TEMPLATE );
69         if (!template)
70         {
71             ERR("no template property!\n");
72             msiobj_release( &si->hdr );
73             goto end;
74         }
75         if (!template[0])
76         {
77             ret = ERROR_SUCCESS;
78             msiobj_release( &si->hdr );
79             goto end;
80         }
81         TRACE("template: %s\n", debugstr_w(template));
82         p = strchrW( template, ';' );
83         if (p && match_language( package, atoiW( p + 1 ) ))
84         {
85             TRACE("applicable transform\n");
86             ret = ERROR_SUCCESS;
87         }
88         /* FIXME: check platform */
89         msiobj_release( &si->hdr );
90     }
91
92 end:
93     msi_free( patch_product );
94     msi_free( prod_code );
95     msi_free( template );
96     return ret;
97 }
98
99 static UINT apply_substorage_transform( MSIPACKAGE *package, MSIDATABASE *patch_db, LPCWSTR name )
100 {
101     UINT ret = ERROR_FUNCTION_FAILED;
102     IStorage *stg = NULL;
103     HRESULT r;
104
105     TRACE("%p %s\n", package, debugstr_w(name));
106
107     if (*name++ != ':')
108     {
109         ERR("expected a colon in %s\n", debugstr_w(name));
110         return ERROR_FUNCTION_FAILED;
111     }
112     r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
113     if (SUCCEEDED(r))
114     {
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 );
121     }
122     else
123     {
124         ERR("failed to open substorage %s\n", debugstr_w(name));
125     }
126     return ERROR_SUCCESS;
127 }
128
129 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
130 {
131     LPWSTR guid_list, *guids, product_code;
132     UINT i, ret = ERROR_FUNCTION_FAILED;
133
134     product_code = msi_dup_property( package->db, szProductCode );
135     if (!product_code)
136     {
137         /* FIXME: the property ProductCode should be written into the DB somewhere */
138         ERR("no product code to check\n");
139         return ERROR_SUCCESS;
140     }
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++)
144     {
145         if (!strcmpW( guids[i], product_code )) ret = ERROR_SUCCESS;
146     }
147     msi_free( guids );
148     msi_free( guid_list );
149     msi_free( product_code );
150     return ret;
151 }
152
153 static UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
154 {
155     MSIPATCHINFO *pi;
156     UINT r = ERROR_SUCCESS;
157     WCHAR *p;
158
159     if (!(pi = msi_alloc_zero( sizeof(MSIPATCHINFO) )))
160     {
161         return ERROR_OUTOFMEMORY;
162     }
163     if (!(pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER )))
164     {
165         msi_free( pi );
166         return ERROR_OUTOFMEMORY;
167     }
168     p = pi->patchcode;
169     if (*p != '{')
170     {
171         msi_free( pi->patchcode );
172         msi_free( pi );
173         return ERROR_PATCH_PACKAGE_INVALID;
174     }
175     if (!(p = strchrW( p + 1, '}' )))
176     {
177         msi_free( pi->patchcode );
178         msi_free( pi );
179         return ERROR_PATCH_PACKAGE_INVALID;
180     }
181     if (p[1])
182     {
183         FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
184         p[1] = 0;
185     }
186     TRACE("patch code %s\n", debugstr_w(pi->patchcode));
187
188     if (!(pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR )))
189     {
190         msi_free( pi->patchcode );
191         msi_free( pi );
192         return ERROR_OUTOFMEMORY;
193     }
194     *patch = pi;
195     return r;
196 }
197
198 static UINT patch_set_media_source_prop( MSIPACKAGE *package )
199 {
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;
209
210     r = MSI_DatabaseOpenViewW( package->db, query, &view );
211     if (r != ERROR_SUCCESS)
212         return r;
213
214     r = MSI_ViewExecute( view, 0 );
215     if (r != ERROR_SUCCESS)
216         goto done;
217
218     if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
219     {
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 );
225     }
226
227 done:
228     msiobj_release( &view->hdr );
229     return r;
230 }
231
232 struct patch_offset
233 {
234     struct list entry;
235     WCHAR *name;
236     UINT sequence;
237 };
238
239 struct patch_offset_list
240 {
241     struct list files;
242     UINT count, min, max;
243     UINT offset_to_apply;
244 };
245
246 static struct patch_offset_list *patch_offset_list_create( void )
247 {
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;
253 }
254
255 static void patch_offset_list_free( struct patch_offset_list *pos )
256 {
257     struct patch_offset *po, *po2;
258
259     LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct patch_offset, entry )
260     {
261         msi_free( po->name );
262         msi_free( po );
263     }
264     msi_free( pos );
265 }
266
267 static void patch_offset_get_patches( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
268 {
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;
276
277     r = MSI_DatabaseOpenViewW( db, query, &view );
278     if (r != ERROR_SUCCESS)
279         return;
280
281     rec = MSI_CreateRecord( 1 );
282     MSI_RecordSetInteger( rec, 1, last_sequence );
283
284     r = MSI_ViewExecute( view, rec );
285     msiobj_release( &rec->hdr );
286     if (r != ERROR_SUCCESS)
287         return;
288
289     while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
290     {
291         UINT sequence = MSI_RecordGetInteger( rec, 2 );
292
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 );
298     }
299     msiobj_release( &view->hdr );
300 }
301
302 static void patch_offset_get_files( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
303 {
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;
311
312     r = MSI_DatabaseOpenViewW( db, query, &view );
313     if (r != ERROR_SUCCESS)
314         return;
315
316     rec = MSI_CreateRecord( 1 );
317     MSI_RecordSetInteger( rec, 1, last_sequence );
318
319     r = MSI_ViewExecute( view, rec );
320     msiobj_release( &rec->hdr );
321     if (r != ERROR_SUCCESS)
322         return;
323
324     while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
325     {
326         UINT attributes = MSI_RecordGetInteger( rec, 7 );
327         if (attributes & msidbFileAttributesPatchAdded)
328         {
329             struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) );
330
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++;
337         }
338         msiobj_release( &rec->hdr );
339     }
340     msiobj_release( &view->hdr );
341 }
342
343 static UINT patch_offset_modify_db( MSIDATABASE *db, struct patch_offset_list *pos )
344 {
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;
354
355     r = MSI_DatabaseOpenViewW( db, query, &view );
356     if (r != ERROR_SUCCESS)
357         return ERROR_SUCCESS;
358
359     rec = MSI_CreateRecord( 2 );
360     MSI_RecordSetInteger( rec, 1, pos->min );
361     MSI_RecordSetInteger( rec, 2, pos->max );
362
363     r = MSI_ViewExecute( view, rec );
364     msiobj_release( &rec->hdr );
365     if (r != ERROR_SUCCESS)
366         goto done;
367
368     LIST_FOR_EACH_ENTRY( po, &pos->files, struct patch_offset, entry )
369     {
370         UINT r_fetch;
371         while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
372         {
373             const WCHAR *file = MSI_RecordGetString( rec, 1 );
374             UINT seq;
375
376             if (!strcmpiW( file, po->name ))
377             {
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;
386             }
387             msiobj_release( &rec->hdr );
388         }
389         if (r_fetch != ERROR_SUCCESS) break;
390     }
391
392 done:
393     msiobj_release( &view->hdr );
394     return ERROR_SUCCESS;
395 }
396
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};
402
403 struct patch_media
404 {
405     struct list entry;
406     UINT    disk_id;
407     UINT    last_sequence;
408     WCHAR  *prompt;
409     WCHAR  *cabinet;
410     WCHAR  *volume;
411     WCHAR  *source;
412 };
413
414 static UINT add_patch_media( MSIPACKAGE *package, IStorage *patch )
415 {
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;
430
431     r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view );
432     if (r != ERROR_SUCCESS) return r;
433
434     r = MSI_ViewExecute( view, 0 );
435     if (r != ERROR_SUCCESS)
436     {
437         msiobj_release( &view->hdr );
438         TRACE("query failed %u\n", r);
439         return r;
440     }
441     list_init( &media_list );
442     while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
443     {
444         disk_id = MSI_RecordGetInteger( rec, 1 );
445         TRACE("disk_id %u\n", disk_id);
446         if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
447         {
448             msiobj_release( &rec->hdr );
449             continue;
450         }
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 );
458
459         list_add_tail( &media_list, &media->entry );
460         msiobj_release( &rec->hdr );
461     }
462     LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry )
463     {
464         MSIQUERY *delete_view, *insert_view;
465
466         r = MSI_DatabaseOpenViewW( package->db, delete_query, &delete_view );
467         if (r != ERROR_SUCCESS) goto done;
468
469         rec = MSI_CreateRecord( 1 );
470         MSI_RecordSetInteger( rec, 1, media->disk_id );
471
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;
476
477         r = MSI_DatabaseOpenViewW( package->db, insert_query, &insert_view );
478         if (r != ERROR_SUCCESS) goto done;
479
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));
487
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 );
495
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;
500
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++;
504     }
505
506 done:
507     msiobj_release( &view->hdr );
508     LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry )
509     {
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 );
516     }
517     return r;
518 }
519
520 static UINT set_patch_offsets( MSIDATABASE *db )
521 {
522     MSIQUERY *view;
523     MSIRECORD *rec;
524     UINT r;
525
526     r = MSI_DatabaseOpenViewW( db, patch_media_query, &view );
527     if (r != ERROR_SUCCESS)
528         return r;
529
530     r = MSI_ViewExecute( view, 0 );
531     if (r != ERROR_SUCCESS)
532         goto done;
533
534     while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
535     {
536         UINT last_sequence = MSI_RecordGetInteger( rec, 2 );
537         struct patch_offset_list *pos;
538
539         /* FIXME: set/check Source field instead? */
540         if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
541         {
542             msiobj_release( &rec->hdr );
543             continue;
544         }
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)
549         {
550             UINT offset = db->media_transform_offset - pos->min;
551             last_sequence = offset + pos->max;
552
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 );
557
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;
563         }
564         patch_offset_list_free( pos );
565         msiobj_release( &rec->hdr );
566     }
567
568 done:
569     msiobj_release( &view->hdr );
570     return r;
571 }
572
573 static UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
574 {
575     UINT i, r = ERROR_SUCCESS;
576     WCHAR **substorage;
577
578     /* apply substorage transforms */
579     substorage = msi_split_string( patch->transforms, ';' );
580     for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
581     {
582         r = apply_substorage_transform( package, patch_db, substorage[i] );
583         if (r == ERROR_SUCCESS)
584         {
585             add_patch_media( package, patch_db->storage );
586             set_patch_offsets( package->db );
587         }
588     }
589     msi_free( substorage );
590     if (r != ERROR_SUCCESS)
591         return r;
592
593     patch_set_media_source_prop( package );
594
595     patch->state = MSIPATCHSTATE_APPLIED;
596     list_add_tail( &package->patches, &patch->entry );
597     return ERROR_SUCCESS;
598 }
599
600 static UINT msi_apply_patch_package( MSIPACKAGE *package, const WCHAR *file )
601 {
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;
608
609     TRACE("%p %s\n", package, debugstr_w(file));
610
611     r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
612     if (r != ERROR_SUCCESS)
613     {
614         ERR("failed to open patch collection %s\n", debugstr_w( file ) );
615         return r;
616     }
617     if (!(si = MSI_GetSummaryInformationW( patch_db->storage, 0 )))
618     {
619         msiobj_release( &patch_db->hdr );
620         return ERROR_FUNCTION_FAILED;
621     }
622     r = msi_check_patch_applicable( package, si );
623     if (r != ERROR_SUCCESS)
624     {
625         TRACE("patch not applicable\n");
626         r = ERROR_SUCCESS;
627         goto done;
628     }
629     r = msi_parse_patch_summary( si, &patch );
630     if ( r != ERROR_SUCCESS )
631         goto done;
632
633     r = msi_get_local_package_name( localfile, dotmsp );
634     if ( r != ERROR_SUCCESS )
635         goto done;
636
637     r = ERROR_OUTOFMEMORY;
638     if (!(patch->filename = strdupW( file ))) goto done;
639     if (!(patch->localfile = strdupW( localfile ))) goto done;
640
641     r = msi_apply_patch_db( package, patch_db, patch );
642     if (r != ERROR_SUCCESS) WARN("patch failed to apply %u\n", r);
643
644 done:
645     msiobj_release( &si->hdr );
646     msiobj_release( &patch_db->hdr );
647     if (patch && r != ERROR_SUCCESS)
648     {
649         msi_free( patch->patchcode );
650         msi_free( patch->transforms );
651         msi_free( patch->filename );
652         msi_free( patch->localfile );
653         msi_free( patch );
654     }
655     return r;
656 }
657
658 /* get the PATCH property, and apply all the patches it specifies */
659 UINT msi_apply_patches( MSIPACKAGE *package )
660 {
661     LPWSTR patch_list, *patches;
662     UINT i, r = ERROR_SUCCESS;
663
664     patch_list = msi_dup_property( package->db, szPatch );
665
666     TRACE("patches to be applied: %s\n", debugstr_w(patch_list));
667
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] );
671
672     msi_free( patches );
673     msi_free( patch_list );
674     return r;
675 }
676
677 UINT msi_apply_transforms( MSIPACKAGE *package )
678 {
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;
682
683     xform_list = msi_dup_property( package->db, szTransforms );
684     xforms = msi_split_string( xform_list, ';' );
685
686     for (i = 0; xforms && xforms[i] && r == ERROR_SUCCESS; i++)
687     {
688         if (xforms[i][0] == ':')
689             r = apply_substorage_transform( package, package->db, xforms[i] );
690         else
691         {
692             WCHAR *transform;
693
694             if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
695             else
696             {
697                 WCHAR *p = strrchrW( package->PackagePath, '\\' );
698                 DWORD len = p - package->PackagePath + 1;
699
700                 if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
701                 {
702                     msi_free( xforms );
703                     msi_free( xform_list );
704                     return ERROR_OUTOFMEMORY;
705                 }
706                 memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
707                 memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
708             }
709             r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
710             if (transform != xforms[i]) msi_free( transform );
711         }
712     }
713     msi_free( xforms );
714     msi_free( xform_list );
715     return r;
716 }
717
718 UINT msi_apply_registered_patch( MSIPACKAGE *package, LPCWSTR patch_code )
719 {
720     UINT r;
721     DWORD len;
722     WCHAR patch_file[MAX_PATH];
723     MSIDATABASE *patch_db;
724     MSIPATCHINFO *patch_info;
725     MSISUMMARYINFO *si;
726
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)
731     {
732         ERR("failed to get patch filename %u\n", r);
733         return r;
734     }
735     r = MSI_OpenDatabaseW( patch_file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
736     if (r != ERROR_SUCCESS)
737     {
738         ERR("failed to open patch database %s\n", debugstr_w( patch_file ));
739         return r;
740     }
741     si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
742     if (!si)
743     {
744         msiobj_release( &patch_db->hdr );
745         return ERROR_FUNCTION_FAILED;
746     }
747     r = msi_parse_patch_summary( si, &patch_info );
748     msiobj_release( &si->hdr );
749     if (r != ERROR_SUCCESS)
750     {
751         ERR("failed to parse patch summary %u\n", r);
752         msiobj_release( &patch_db->hdr );
753         return r;
754     }
755     patch_info->localfile = strdupW( patch_file );
756     if (!patch_info->localfile)
757     {
758         msiobj_release( &patch_db->hdr );
759         return ERROR_OUTOFMEMORY;
760     }
761     r = msi_apply_patch_db( package, patch_db, patch_info );
762     msiobj_release( &patch_db->hdr );
763     if (r != ERROR_SUCCESS)
764     {
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 );
770     }
771     return r;
772 }