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