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