advapi32: Fix the parameter checks in QueryServiceStatusEx.
[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 UINT check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
36 {
37     static const WCHAR szSystemLanguageID[] = {
38         'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0};
39     LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
40     UINT ret = ERROR_FUNCTION_FAILED;
41
42     prod_code = msi_dup_property( package->db, szProductCode );
43     patch_product = msi_get_suminfo_product( patch );
44
45     TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
46
47     if (strstrW( patch_product, prod_code ))
48     {
49         MSISUMMARYINFO *si;
50         const WCHAR *p;
51
52         si = MSI_GetSummaryInformationW( patch, 0 );
53         if (!si)
54         {
55             ERR("no summary information!\n");
56             goto end;
57         }
58         template = msi_suminfo_dup_string( si, PID_TEMPLATE );
59         if (!template)
60         {
61             ERR("no template property!\n");
62             msiobj_release( &si->hdr );
63             goto end;
64         }
65         if (!template[0])
66         {
67             ret = ERROR_SUCCESS;
68             msiobj_release( &si->hdr );
69             goto end;
70         }
71         langid = msi_dup_property( package->db, szSystemLanguageID );
72         if (!langid)
73         {
74             msiobj_release( &si->hdr );
75             goto end;
76         }
77         p = strchrW( template, ';' );
78         if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
79         {
80             TRACE("applicable transform\n");
81             ret = ERROR_SUCCESS;
82         }
83         /* FIXME: check platform */
84         msiobj_release( &si->hdr );
85     }
86
87 end:
88     msi_free( patch_product );
89     msi_free( prod_code );
90     msi_free( template );
91     msi_free( langid );
92     return ret;
93 }
94
95 static UINT apply_substorage_transform( MSIPACKAGE *package, MSIDATABASE *patch_db, LPCWSTR name )
96 {
97     UINT ret = ERROR_FUNCTION_FAILED;
98     IStorage *stg = NULL;
99     HRESULT r;
100
101     TRACE("%p %s\n", package, debugstr_w(name));
102
103     if (*name++ != ':')
104     {
105         ERR("expected a colon in %s\n", debugstr_w(name));
106         return ERROR_FUNCTION_FAILED;
107     }
108     r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
109     if (SUCCEEDED(r))
110     {
111         ret = check_transform_applicable( package, stg );
112         if (ret == ERROR_SUCCESS)
113             msi_table_apply_transform( package->db, stg );
114         else
115             TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
116         IStorage_Release( stg );
117     }
118     else
119     {
120         ERR("failed to open substorage %s\n", debugstr_w(name));
121     }
122     return ERROR_SUCCESS;
123 }
124
125 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
126 {
127     LPWSTR guid_list, *guids, product_code;
128     UINT i, ret = ERROR_FUNCTION_FAILED;
129
130     product_code = msi_dup_property( package->db, szProductCode );
131     if (!product_code)
132     {
133         /* FIXME: the property ProductCode should be written into the DB somewhere */
134         ERR("no product code to check\n");
135         return ERROR_SUCCESS;
136     }
137     guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
138     guids = msi_split_string( guid_list, ';' );
139     for (i = 0; guids[i] && ret != ERROR_SUCCESS; i++)
140     {
141         if (!strcmpW( guids[i], product_code )) ret = ERROR_SUCCESS;
142     }
143     msi_free( guids );
144     msi_free( guid_list );
145     msi_free( product_code );
146     return ret;
147 }
148
149 UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
150 {
151     MSIPATCHINFO *pi;
152     UINT r = ERROR_SUCCESS;
153     WCHAR *p;
154
155     if (!(pi = msi_alloc_zero( sizeof(MSIPATCHINFO) )))
156     {
157         return ERROR_OUTOFMEMORY;
158     }
159     if (!(pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER )))
160     {
161         msi_free( pi );
162         return ERROR_OUTOFMEMORY;
163     }
164     p = pi->patchcode;
165     if (*p != '{')
166     {
167         msi_free( pi->patchcode );
168         msi_free( pi );
169         return ERROR_PATCH_PACKAGE_INVALID;
170     }
171     if (!(p = strchrW( p + 1, '}' )))
172     {
173         msi_free( pi->patchcode );
174         msi_free( pi );
175         return ERROR_PATCH_PACKAGE_INVALID;
176     }
177     if (p[1])
178     {
179         FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
180         p[1] = 0;
181     }
182     TRACE("patch code %s\n", debugstr_w(pi->patchcode));
183
184     if (!(pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR )))
185     {
186         msi_free( pi->patchcode );
187         msi_free( pi );
188         return ERROR_OUTOFMEMORY;
189     }
190     *patch = pi;
191     return r;
192 }
193
194 static UINT patch_set_media_source_prop( MSIPACKAGE *package )
195 {
196     static const WCHAR query[] = {
197         'S','E','L','E','C','T',' ','`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
198         '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ',
199         'I','S',' ','N','O','T',' ','N','U','L','L',0};
200     MSIQUERY *view;
201     MSIRECORD *rec;
202     const WCHAR *property;
203     WCHAR *patch;
204     UINT r;
205
206     r = MSI_DatabaseOpenViewW( package->db, query, &view );
207     if (r != ERROR_SUCCESS)
208         return r;
209
210     r = MSI_ViewExecute( view, 0 );
211     if (r != ERROR_SUCCESS)
212         goto done;
213
214     if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
215     {
216         property = MSI_RecordGetString( rec, 1 );
217         patch = msi_dup_property( package->db, szPatch );
218         msi_set_property( package->db, property, patch );
219         msi_free( patch );
220         msiobj_release( &rec->hdr );
221     }
222
223 done:
224     msiobj_release( &view->hdr );
225     return r;
226 }
227
228 struct patch_offset
229 {
230     struct list entry;
231     WCHAR *name;
232     UINT sequence;
233 };
234
235 struct patch_offset_list
236 {
237     struct list files;
238     UINT count, min, max;
239     UINT offset_to_apply;
240 };
241
242 static struct patch_offset_list *patch_offset_list_create( void )
243 {
244     struct patch_offset_list *pos = msi_alloc( sizeof(struct patch_offset_list) );
245     list_init( &pos->files );
246     pos->count = pos->max = 0;
247     pos->min = 999999;
248     return pos;
249 }
250
251 static void patch_offset_list_free( struct patch_offset_list *pos )
252 {
253     struct patch_offset *po, *po2;
254
255     LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct patch_offset, entry )
256     {
257         msi_free( po->name );
258         msi_free( po );
259     }
260     msi_free( pos );
261 }
262
263 static void patch_offset_get_patches( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
264 {
265     static const WCHAR query_patch[] = {
266         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
267         'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
268         'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
269     MSIQUERY *view;
270     MSIRECORD *rec;
271     UINT r;
272
273     r = MSI_DatabaseOpenViewW( db, query_patch, &view );
274     if (r != ERROR_SUCCESS)
275         return;
276
277     rec = MSI_CreateRecord( 1 );
278     MSI_RecordSetInteger( rec, 1, last_sequence );
279
280     r = MSI_ViewExecute( view, rec );
281     msiobj_release( &rec->hdr );
282     if (r != ERROR_SUCCESS)
283         return;
284
285     while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
286     {
287         UINT sequence = MSI_RecordGetInteger( rec, 2 );
288
289         /* FIXME: we only use the max/min sequence numbers for now */
290         pos->min = min( pos->min, sequence );
291         pos->max = max( pos->max, sequence );
292         pos->count++;
293         msiobj_release( &rec->hdr );
294     }
295     msiobj_release( &view->hdr );
296 }
297
298 static void patch_offset_get_files( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
299 {
300     static const WCHAR query_files[] = {
301         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
302         'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
303         'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
304     MSIQUERY *view;
305     MSIRECORD *rec;
306     UINT r;
307
308     r = MSI_DatabaseOpenViewW( db, query_files, &view );
309     if (r != ERROR_SUCCESS)
310         return;
311
312     rec = MSI_CreateRecord( 1 );
313     MSI_RecordSetInteger( rec, 1, last_sequence );
314
315     r = MSI_ViewExecute( view, rec );
316     msiobj_release( &rec->hdr );
317     if (r != ERROR_SUCCESS)
318         return;
319
320     while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
321     {
322         UINT attributes = MSI_RecordGetInteger( rec, 7 );
323         if (attributes & msidbFileAttributesPatchAdded)
324         {
325             struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) );
326
327             po->name     = msi_dup_record_field( rec, 1 );
328             po->sequence = MSI_RecordGetInteger( rec, 8 );
329             pos->min     = min( pos->min, po->sequence );
330             pos->max     = max( pos->max, po->sequence );
331             list_add_tail( &pos->files, &po->entry );
332             pos->count++;
333         }
334         msiobj_release( &rec->hdr );
335     }
336     msiobj_release( &view->hdr );
337 }
338
339 static UINT patch_offset_modify_db( MSIDATABASE *db, struct patch_offset_list *pos )
340 {
341     static const WCHAR query_files[] = {
342         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
343         'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','>','=',' ','?',' ',
344         'A','N','D',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
345         'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
346     struct patch_offset *po;
347     MSIRECORD *rec;
348     MSIQUERY *view;
349     UINT r;
350
351     r = MSI_DatabaseOpenViewW( db, query_files, &view );
352     if (r != ERROR_SUCCESS)
353         return ERROR_SUCCESS;
354
355     rec = MSI_CreateRecord( 2 );
356     MSI_RecordSetInteger( rec, 1, pos->min );
357     MSI_RecordSetInteger( rec, 2, pos->max );
358
359     r = MSI_ViewExecute( view, rec );
360     msiobj_release( &rec->hdr );
361     if (r != ERROR_SUCCESS)
362         goto done;
363
364     LIST_FOR_EACH_ENTRY( po, &pos->files, struct patch_offset, entry )
365     {
366         UINT r_fetch;
367         while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
368         {
369             const WCHAR *file = MSI_RecordGetString( rec, 1 );
370             UINT seq;
371
372             if (!strcmpiW( file, po->name ))
373             {
374                 /* update record */
375                 seq = MSI_RecordGetInteger( rec, 8 );
376                 MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply );
377                 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
378                 if (r != ERROR_SUCCESS)
379                     ERR("Failed to update offset for file %s\n", debugstr_w(file));
380                 msiobj_release( &rec->hdr );
381                 break;
382             }
383             msiobj_release( &rec->hdr );
384         }
385         if (r_fetch != ERROR_SUCCESS) break;
386     }
387
388 done:
389     msiobj_release( &view->hdr );
390     return ERROR_SUCCESS;
391 }
392
393 static const WCHAR patch_media_query[] = {
394     'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
395     'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
396     'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
397     'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
398
399 struct patch_media
400 {
401     struct list entry;
402     UINT    disk_id;
403     UINT    last_sequence;
404     WCHAR  *prompt;
405     WCHAR  *cabinet;
406     WCHAR  *volume;
407     WCHAR  *source;
408 };
409
410 static UINT add_patch_media( MSIPACKAGE *package, IStorage *patch )
411 {
412     static const WCHAR delete_query[] = {
413         'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
414         'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0};
415     static const WCHAR insert_query[] = {
416         'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ',
417         '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',',
418         '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',',
419         '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ',
420         'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
421     MSIQUERY *view;
422     MSIRECORD *rec;
423     UINT r, disk_id;
424     struct list media_list;
425     struct patch_media *media, *next;
426
427     r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view );
428     if (r != ERROR_SUCCESS) return r;
429
430     r = MSI_ViewExecute( view, 0 );
431     if (r != ERROR_SUCCESS)
432     {
433         msiobj_release( &view->hdr );
434         TRACE("query failed %u\n", r);
435         return r;
436     }
437     list_init( &media_list );
438     while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
439     {
440         disk_id = MSI_RecordGetInteger( rec, 1 );
441         TRACE("disk_id %u\n", disk_id);
442         if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
443         {
444             msiobj_release( &rec->hdr );
445             continue;
446         }
447         if (!(media = msi_alloc( sizeof( *media )))) goto done;
448         media->disk_id = disk_id;
449         media->last_sequence = MSI_RecordGetInteger( rec, 2 );
450         media->prompt  = msi_dup_record_field( rec, 3 );
451         media->cabinet = msi_dup_record_field( rec, 4 );
452         media->volume  = msi_dup_record_field( rec, 5 );
453         media->source  = msi_dup_record_field( rec, 6 );
454
455         list_add_tail( &media_list, &media->entry );
456         msiobj_release( &rec->hdr );
457     }
458     LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry )
459     {
460         MSIQUERY *delete_view, *insert_view;
461
462         r = MSI_DatabaseOpenViewW( package->db, delete_query, &delete_view );
463         if (r != ERROR_SUCCESS) goto done;
464
465         rec = MSI_CreateRecord( 1 );
466         MSI_RecordSetInteger( rec, 1, media->disk_id );
467
468         r = MSI_ViewExecute( delete_view, rec );
469         msiobj_release( &delete_view->hdr );
470         msiobj_release( &rec->hdr );
471         if (r != ERROR_SUCCESS) goto done;
472
473         r = MSI_DatabaseOpenViewW( package->db, insert_query, &insert_view );
474         if (r != ERROR_SUCCESS) goto done;
475
476         disk_id = package->db->media_transform_disk_id;
477         TRACE("disk id       %u\n", disk_id);
478         TRACE("last sequence %u\n", media->last_sequence);
479         TRACE("prompt        %s\n", debugstr_w(media->prompt));
480         TRACE("cabinet       %s\n", debugstr_w(media->cabinet));
481         TRACE("volume        %s\n", debugstr_w(media->volume));
482         TRACE("source        %s\n", debugstr_w(media->source));
483
484         rec = MSI_CreateRecord( 6 );
485         MSI_RecordSetInteger( rec, 1, disk_id );
486         MSI_RecordSetInteger( rec, 2, media->last_sequence );
487         MSI_RecordSetStringW( rec, 3, media->prompt );
488         MSI_RecordSetStringW( rec, 4, media->cabinet );
489         MSI_RecordSetStringW( rec, 5, media->volume );
490         MSI_RecordSetStringW( rec, 6, media->source );
491
492         r = MSI_ViewExecute( insert_view, rec );
493         msiobj_release( &insert_view->hdr );
494         msiobj_release( &rec->hdr );
495         if (r != ERROR_SUCCESS) goto done;
496
497         r = msi_add_cabinet_stream( package, disk_id, patch, media->cabinet );
498         if (r != ERROR_SUCCESS) WARN("failed to add cabinet stream %u\n", r);
499         package->db->media_transform_disk_id++;
500     }
501
502 done:
503     msiobj_release( &view->hdr );
504     LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry )
505     {
506         list_remove( &media->entry );
507         msi_free( media->prompt );
508         msi_free( media->cabinet );
509         msi_free( media->volume );
510         msi_free( media->source );
511         msi_free( media );
512     }
513     return r;
514 }
515
516 static UINT set_patch_offsets( MSIDATABASE *db )
517 {
518     MSIQUERY *view;
519     MSIRECORD *rec;
520     UINT r;
521
522     r = MSI_DatabaseOpenViewW( db, patch_media_query, &view );
523     if (r != ERROR_SUCCESS)
524         return r;
525
526     r = MSI_ViewExecute( view, 0 );
527     if (r != ERROR_SUCCESS)
528         goto done;
529
530     while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
531     {
532         UINT last_sequence = MSI_RecordGetInteger( rec, 2 );
533         struct patch_offset_list *pos;
534
535         /* FIXME: set/check Source field instead? */
536         if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
537         {
538             msiobj_release( &rec->hdr );
539             continue;
540         }
541         pos = patch_offset_list_create();
542         patch_offset_get_files( db, last_sequence, pos );
543         patch_offset_get_patches( db, last_sequence, pos );
544         if (pos->count)
545         {
546             UINT offset = db->media_transform_offset - pos->min;
547             last_sequence = offset + pos->max;
548
549             /* FIXME: this is for the patch table, which is not yet properly transformed */
550             last_sequence += pos->min;
551             pos->offset_to_apply = offset;
552             patch_offset_modify_db( db, pos );
553
554             MSI_RecordSetInteger( rec, 2, last_sequence );
555             r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
556             if (r != ERROR_SUCCESS)
557                 ERR("Failed to update Media table entry, expect breakage (%u)\n", r);
558             db->media_transform_offset = last_sequence + 1;
559         }
560         patch_offset_list_free( pos );
561         msiobj_release( &rec->hdr );
562     }
563
564 done:
565     msiobj_release( &view->hdr );
566     return r;
567 }
568
569 UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
570 {
571     UINT i, r = ERROR_SUCCESS;
572     WCHAR **substorage;
573
574     /* apply substorage transforms */
575     substorage = msi_split_string( patch->transforms, ';' );
576     for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
577     {
578         r = apply_substorage_transform( package, patch_db, substorage[i] );
579         if (r == ERROR_SUCCESS)
580         {
581             add_patch_media( package, patch_db->storage );
582             set_patch_offsets( package->db );
583         }
584     }
585     msi_free( substorage );
586     if (r != ERROR_SUCCESS)
587         return r;
588
589     patch_set_media_source_prop( package );
590
591     patch->state = MSIPATCHSTATE_APPLIED;
592     list_add_tail( &package->patches, &patch->entry );
593     return ERROR_SUCCESS;
594 }
595
596 static UINT msi_apply_patch_package( MSIPACKAGE *package, const WCHAR *file )
597 {
598     static const WCHAR dotmsp[] = {'.','m','s','p',0};
599     MSIDATABASE *patch_db = NULL;
600     WCHAR localfile[MAX_PATH];
601     MSISUMMARYINFO *si;
602     MSIPATCHINFO *patch = NULL;
603     UINT r = ERROR_SUCCESS;
604
605     TRACE("%p %s\n", package, debugstr_w(file));
606
607     r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
608     if (r != ERROR_SUCCESS)
609     {
610         ERR("failed to open patch collection %s\n", debugstr_w( file ) );
611         return r;
612     }
613     if (!(si = MSI_GetSummaryInformationW( patch_db->storage, 0 )))
614     {
615         msiobj_release( &patch_db->hdr );
616         return ERROR_FUNCTION_FAILED;
617     }
618     r = msi_check_patch_applicable( package, si );
619     if (r != ERROR_SUCCESS)
620     {
621         TRACE("patch not applicable\n");
622         r = ERROR_SUCCESS;
623         goto done;
624     }
625     r = msi_parse_patch_summary( si, &patch );
626     if ( r != ERROR_SUCCESS )
627         goto done;
628
629     r = msi_get_local_package_name( localfile, dotmsp );
630     if ( r != ERROR_SUCCESS )
631         goto done;
632
633     TRACE("copying to local package %s\n", debugstr_w(localfile));
634
635     if (!CopyFileW( file, localfile, FALSE ))
636     {
637         ERR("Unable to copy package (%s -> %s) (error %u)\n",
638             debugstr_w(file), debugstr_w(localfile), GetLastError());
639         r = GetLastError();
640         goto done;
641     }
642     patch->localfile = strdupW( localfile );
643
644     r = msi_apply_patch_db( package, patch_db, patch );
645     if (r != ERROR_SUCCESS) WARN("patch failed to apply %u\n", r);
646
647 done:
648     msiobj_release( &si->hdr );
649     msiobj_release( &patch_db->hdr );
650     if (patch && r != ERROR_SUCCESS)
651     {
652         if (patch->localfile) DeleteFileW( patch->localfile );
653         msi_free( patch->patchcode );
654         msi_free( patch->transforms );
655         msi_free( patch->localfile );
656         msi_free( patch );
657     }
658     return r;
659 }
660
661 /* get the PATCH property, and apply all the patches it specifies */
662 UINT msi_apply_patches( MSIPACKAGE *package )
663 {
664     LPWSTR patch_list, *patches;
665     UINT i, r = ERROR_SUCCESS;
666
667     patch_list = msi_dup_property( package->db, szPatch );
668
669     TRACE("patches to be applied: %s\n", debugstr_w(patch_list));
670
671     patches = msi_split_string( patch_list, ';' );
672     for (i = 0; patches && patches[i] && r == ERROR_SUCCESS; i++)
673         r = msi_apply_patch_package( package, patches[i] );
674
675     msi_free( patches );
676     msi_free( patch_list );
677     return r;
678 }
679
680 UINT msi_apply_transforms( MSIPACKAGE *package )
681 {
682     static const WCHAR szTransforms[] = {'T','R','A','N','S','F','O','R','M','S',0};
683     LPWSTR xform_list, *xforms;
684     UINT i, r = ERROR_SUCCESS;
685
686     xform_list = msi_dup_property( package->db, szTransforms );
687     xforms = msi_split_string( xform_list, ';' );
688
689     for (i = 0; xforms && xforms[i] && r == ERROR_SUCCESS; i++)
690     {
691         if (xforms[i][0] == ':')
692             r = apply_substorage_transform( package, package->db, xforms[i] );
693         else
694         {
695             WCHAR *transform;
696
697             if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
698             else
699             {
700                 WCHAR *p = strrchrW( package->PackagePath, '\\' );
701                 DWORD len = p - package->PackagePath + 1;
702
703                 if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
704                 {
705                     msi_free( xforms );
706                     msi_free( xform_list );
707                     return ERROR_OUTOFMEMORY;
708                 }
709                 memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
710                 memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
711             }
712             r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
713             if (transform != xforms[i]) msi_free( transform );
714         }
715     }
716     msi_free( xforms );
717     msi_free( xform_list );
718     return r;
719 }