kernel32/tests: Add tabular UTF-7 encoding tests.
[wine.git] / dlls / msi / patch.c
bloba738de1ee3eaf0242b996bf4fb10f36c3056c521
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
5 * Copyright 2011 Hans Leidekker for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <stdarg.h>
23 #define COBJMACROS
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winreg.h"
27 #include "objbase.h"
28 #include "shlwapi.h"
29 #include "wine/debug.h"
30 #include "wine/unicode.h"
31 #include "msipriv.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(msi);
35 static BOOL match_language( MSIPACKAGE *package, LANGID langid )
37 UINT i;
39 if (!package->num_langids || !langid) return TRUE;
40 for (i = 0; i < package->num_langids; i++)
42 if (package->langids[i] == langid) return TRUE;
44 return FALSE;
47 struct transform_desc
49 WCHAR *product_code_from;
50 WCHAR *product_code_to;
51 WCHAR *version_from;
52 WCHAR *version_to;
53 WCHAR *upgrade_code;
56 static void free_transform_desc( struct transform_desc *desc )
58 msi_free( desc->product_code_from );
59 msi_free( desc->product_code_to );
60 msi_free( desc->version_from );
61 msi_free( desc->version_to );
62 msi_free( desc->upgrade_code );
63 msi_free( desc );
66 static struct transform_desc *parse_transform_desc( const WCHAR *str )
68 struct transform_desc *ret;
69 const WCHAR *p = str, *q;
70 UINT len;
72 if (!(ret = msi_alloc_zero( sizeof(*ret) ))) return NULL;
74 q = strchrW( p, '}' );
75 if (*p != '{' || !q) goto error;
77 len = q - p + 1;
78 if (!(ret->product_code_from = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error;
79 memcpy( ret->product_code_from, p, len * sizeof(WCHAR) );
80 ret->product_code_from[len] = 0;
82 p = q + 1;
83 if (!(q = strchrW( p, ';' ))) goto error;
84 len = q - p;
85 if (!(ret->version_from = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error;
86 memcpy( ret->version_from, p, len * sizeof(WCHAR) );
87 ret->version_from[len] = 0;
89 p = q + 1;
90 q = strchrW( p, '}' );
91 if (*p != '{' || !q) goto error;
93 len = q - p + 1;
94 if (!(ret->product_code_to = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error;
95 memcpy( ret->product_code_to, p, len * sizeof(WCHAR) );
96 ret->product_code_to[len] = 0;
98 p = q + 1;
99 if (!(q = strchrW( p, ';' ))) goto error;
100 len = q - p;
101 if (!(ret->version_to = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error;
102 memcpy( ret->version_to, p, len * sizeof(WCHAR) );
103 ret->version_to[len] = 0;
105 p = q + 1;
106 q = strchrW( p, '}' );
107 if (*p != '{' || !q) goto error;
109 len = q - p + 1;
110 if (!(ret->upgrade_code = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error;
111 memcpy( ret->upgrade_code, p, len * sizeof(WCHAR) );
112 ret->upgrade_code[len] = 0;
114 return ret;
116 error:
117 free_transform_desc( ret );
118 return NULL;
121 static UINT check_transform_applicable( MSIPACKAGE *package, IStorage *transform )
123 static const UINT supported_flags =
124 MSITRANSFORM_VALIDATE_PRODUCT | MSITRANSFORM_VALIDATE_LANGUAGE |
125 MSITRANSFORM_VALIDATE_PLATFORM | MSITRANSFORM_VALIDATE_MAJORVERSION |
126 MSITRANSFORM_VALIDATE_MINORVERSION | MSITRANSFORM_VALIDATE_UPGRADECODE;
127 MSISUMMARYINFO *si = MSI_GetSummaryInformationW( transform, 0 );
128 UINT valid_flags = 0, wanted_flags = 0;
129 WCHAR *template, *product, *p;
130 struct transform_desc *desc;
132 if (!si)
134 WARN("no summary information!\n");
135 return ERROR_FUNCTION_FAILED;
137 wanted_flags = msi_suminfo_get_int32( si, PID_CHARCOUNT );
138 wanted_flags &= 0xffff; /* mask off error condition flags */
139 TRACE("validation flags 0x%04x\n", wanted_flags);
141 if (wanted_flags & ~supported_flags)
143 FIXME("unsupported validation flags 0x%04x\n", wanted_flags);
144 msiobj_release( &si->hdr );
145 return ERROR_FUNCTION_FAILED;
147 if (!(template = msi_suminfo_dup_string( si, PID_TEMPLATE )))
149 WARN("no template property!\n");
150 msiobj_release( &si->hdr );
151 return ERROR_FUNCTION_FAILED;
153 TRACE("template property: %s\n", debugstr_w(template));
154 if (!(product = msi_get_suminfo_product( transform )))
156 WARN("no product property!\n");
157 msi_free( template );
158 msiobj_release( &si->hdr );
159 return ERROR_FUNCTION_FAILED;
161 TRACE("product property: %s\n", debugstr_w(product));
162 if (!(desc = parse_transform_desc( product )))
164 msi_free( template );
165 msiobj_release( &si->hdr );
166 return ERROR_FUNCTION_FAILED;
168 msi_free( product );
170 if (wanted_flags & MSITRANSFORM_VALIDATE_LANGUAGE)
172 if (!template[0] || ((p = strchrW( template, ';' )) && match_language( package, atoiW( p + 1 ) )))
174 valid_flags |= MSITRANSFORM_VALIDATE_LANGUAGE;
177 if (wanted_flags & MSITRANSFORM_VALIDATE_PRODUCT)
179 WCHAR *product_code_installed = msi_dup_property( package->db, szProductCode );
181 if (!product_code_installed)
183 msi_free( template );
184 free_transform_desc( desc );
185 msiobj_release( &si->hdr );
186 return ERROR_INSTALL_PACKAGE_INVALID;
188 if (!strcmpW( desc->product_code_from, product_code_installed ))
190 valid_flags |= MSITRANSFORM_VALIDATE_PRODUCT;
192 msi_free( product_code_installed );
194 if (wanted_flags & MSITRANSFORM_VALIDATE_PLATFORM)
196 if ((p = strchrW( template, ';' )))
198 *p = 0;
199 if (package->platform == parse_platform( template ))
200 valid_flags |= MSITRANSFORM_VALIDATE_PLATFORM;
203 msi_free( template );
204 if (wanted_flags & MSITRANSFORM_VALIDATE_MAJORVERSION)
206 WCHAR *product_version_installed = msi_dup_property( package->db, szProductVersion );
207 DWORD major_installed, minor_installed, major, minor;
209 if (!product_version_installed)
211 free_transform_desc( desc );
212 msiobj_release( &si->hdr );
213 return ERROR_INSTALL_PACKAGE_INVALID;
215 msi_parse_version_string( product_version_installed, &major_installed, &minor_installed );
216 msi_parse_version_string( desc->version_from, &major, &minor );
218 if (major_installed == major)
220 valid_flags |= MSITRANSFORM_VALIDATE_MAJORVERSION;
221 wanted_flags &= ~MSITRANSFORM_VALIDATE_MINORVERSION;
223 msi_free( product_version_installed );
225 else if (wanted_flags & MSITRANSFORM_VALIDATE_MINORVERSION)
227 WCHAR *product_version_installed = msi_dup_property( package->db, szProductVersion );
228 DWORD major_installed, minor_installed, major, minor;
230 if (!product_version_installed)
232 free_transform_desc( desc );
233 msiobj_release( &si->hdr );
234 return ERROR_INSTALL_PACKAGE_INVALID;
236 msi_parse_version_string( product_version_installed, &major_installed, &minor_installed );
237 msi_parse_version_string( desc->version_from, &major, &minor );
239 if (major_installed == major && minor_installed == minor)
240 valid_flags |= MSITRANSFORM_VALIDATE_MINORVERSION;
241 msi_free( product_version_installed );
243 if (wanted_flags & MSITRANSFORM_VALIDATE_UPGRADECODE)
245 WCHAR *upgrade_code_installed = msi_dup_property( package->db, szUpgradeCode );
247 if (!upgrade_code_installed)
249 free_transform_desc( desc );
250 msiobj_release( &si->hdr );
251 return ERROR_INSTALL_PACKAGE_INVALID;
253 if (!strcmpW( desc->upgrade_code, upgrade_code_installed ))
254 valid_flags |= MSITRANSFORM_VALIDATE_UPGRADECODE;
255 msi_free( upgrade_code_installed );
258 free_transform_desc( desc );
259 msiobj_release( &si->hdr );
260 if ((valid_flags & wanted_flags) != wanted_flags) return ERROR_FUNCTION_FAILED;
261 TRACE("applicable transform\n");
262 return ERROR_SUCCESS;
265 static UINT apply_substorage_transform( MSIPACKAGE *package, MSIDATABASE *patch_db, LPCWSTR name )
267 UINT ret = ERROR_FUNCTION_FAILED;
268 IStorage *stg = NULL;
269 HRESULT r;
271 TRACE("%p %s\n", package, debugstr_w(name));
273 if (*name++ != ':')
275 ERR("expected a colon in %s\n", debugstr_w(name));
276 return ERROR_FUNCTION_FAILED;
278 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
279 if (SUCCEEDED(r))
281 ret = check_transform_applicable( package, stg );
282 if (ret == ERROR_SUCCESS)
283 msi_table_apply_transform( package->db, stg );
284 else
285 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
286 IStorage_Release( stg );
288 else
290 ERR("failed to open substorage %s\n", debugstr_w(name));
292 return ERROR_SUCCESS;
295 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
297 LPWSTR guid_list, *guids, product_code;
298 UINT i, ret = ERROR_FUNCTION_FAILED;
300 product_code = msi_dup_property( package->db, szProductCode );
301 if (!product_code)
303 /* FIXME: the property ProductCode should be written into the DB somewhere */
304 ERR("no product code to check\n");
305 return ERROR_SUCCESS;
307 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
308 guids = msi_split_string( guid_list, ';' );
309 for (i = 0; guids[i] && ret != ERROR_SUCCESS; i++)
311 if (!strcmpW( guids[i], product_code )) ret = ERROR_SUCCESS;
313 msi_free( guids );
314 msi_free( guid_list );
315 msi_free( product_code );
316 return ret;
319 static UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
321 MSIPATCHINFO *pi;
322 UINT r = ERROR_SUCCESS;
323 WCHAR *p;
325 if (!(pi = msi_alloc_zero( sizeof(MSIPATCHINFO) )))
327 return ERROR_OUTOFMEMORY;
329 if (!(pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER )))
331 msi_free( pi );
332 return ERROR_OUTOFMEMORY;
334 p = pi->patchcode;
335 if (*p != '{')
337 msi_free( pi->patchcode );
338 msi_free( pi );
339 return ERROR_PATCH_PACKAGE_INVALID;
341 if (!(p = strchrW( p + 1, '}' )))
343 msi_free( pi->patchcode );
344 msi_free( pi );
345 return ERROR_PATCH_PACKAGE_INVALID;
347 if (p[1])
349 FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
350 p[1] = 0;
352 TRACE("patch code %s\n", debugstr_w(pi->patchcode));
353 if (!(pi->products = msi_suminfo_dup_string( si, PID_TEMPLATE )))
355 msi_free( pi->patchcode );
356 msi_free( pi );
357 return ERROR_OUTOFMEMORY;
359 if (!(pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR )))
361 msi_free( pi->patchcode );
362 msi_free( pi->products );
363 msi_free( pi );
364 return ERROR_OUTOFMEMORY;
366 *patch = pi;
367 return r;
370 static UINT patch_set_media_source_prop( MSIPACKAGE *package )
372 static const WCHAR query[] = {
373 'S','E','L','E','C','T',' ','`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
374 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ',
375 'I','S',' ','N','O','T',' ','N','U','L','L',0};
376 MSIQUERY *view;
377 MSIRECORD *rec;
378 const WCHAR *property;
379 WCHAR *patch;
380 UINT r;
382 r = MSI_DatabaseOpenViewW( package->db, query, &view );
383 if (r != ERROR_SUCCESS)
384 return r;
386 r = MSI_ViewExecute( view, 0 );
387 if (r != ERROR_SUCCESS)
388 goto done;
390 if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
392 property = MSI_RecordGetString( rec, 1 );
393 patch = msi_dup_property( package->db, szPatch );
394 msi_set_property( package->db, property, patch, -1 );
395 msi_free( patch );
396 msiobj_release( &rec->hdr );
399 done:
400 msiobj_release( &view->hdr );
401 return r;
404 struct patch_offset
406 struct list entry;
407 WCHAR *name;
408 UINT sequence;
411 struct patch_offset_list
413 struct list files;
414 UINT count, min, max;
415 UINT offset_to_apply;
418 static struct patch_offset_list *patch_offset_list_create( void )
420 struct patch_offset_list *pos = msi_alloc( sizeof(struct patch_offset_list) );
421 list_init( &pos->files );
422 pos->count = pos->max = 0;
423 pos->min = 999999;
424 return pos;
427 static void patch_offset_list_free( struct patch_offset_list *pos )
429 struct patch_offset *po, *po2;
431 LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct patch_offset, entry )
433 msi_free( po->name );
434 msi_free( po );
436 msi_free( pos );
439 static void patch_offset_get_patches( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
441 static const WCHAR query[] = {
442 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
443 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
444 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
445 MSIQUERY *view;
446 MSIRECORD *rec;
447 UINT r;
449 r = MSI_DatabaseOpenViewW( db, query, &view );
450 if (r != ERROR_SUCCESS)
451 return;
453 rec = MSI_CreateRecord( 1 );
454 MSI_RecordSetInteger( rec, 1, last_sequence );
456 r = MSI_ViewExecute( view, rec );
457 msiobj_release( &rec->hdr );
458 if (r != ERROR_SUCCESS)
459 return;
461 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
463 UINT sequence = MSI_RecordGetInteger( rec, 2 );
465 /* FIXME: we only use the max/min sequence numbers for now */
466 pos->min = min( pos->min, sequence );
467 pos->max = max( pos->max, sequence );
468 pos->count++;
469 msiobj_release( &rec->hdr );
471 msiobj_release( &view->hdr );
474 static void patch_offset_get_files( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
476 static const WCHAR query[] = {
477 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
478 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
479 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
480 MSIQUERY *view;
481 MSIRECORD *rec;
482 UINT r;
484 r = MSI_DatabaseOpenViewW( db, query, &view );
485 if (r != ERROR_SUCCESS)
486 return;
488 rec = MSI_CreateRecord( 1 );
489 MSI_RecordSetInteger( rec, 1, last_sequence );
491 r = MSI_ViewExecute( view, rec );
492 msiobj_release( &rec->hdr );
493 if (r != ERROR_SUCCESS)
494 return;
496 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
498 UINT attributes = MSI_RecordGetInteger( rec, 7 );
499 if (attributes & msidbFileAttributesPatchAdded)
501 struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) );
503 po->name = msi_dup_record_field( rec, 1 );
504 po->sequence = MSI_RecordGetInteger( rec, 8 );
505 pos->min = min( pos->min, po->sequence );
506 pos->max = max( pos->max, po->sequence );
507 list_add_tail( &pos->files, &po->entry );
508 pos->count++;
510 msiobj_release( &rec->hdr );
512 msiobj_release( &view->hdr );
515 static UINT patch_offset_modify_db( MSIDATABASE *db, struct patch_offset_list *pos )
517 static const WCHAR query[] = {
518 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
519 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','>','=',' ','?',' ',
520 'A','N','D',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
521 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
522 struct patch_offset *po;
523 MSIRECORD *rec;
524 MSIQUERY *view;
525 UINT r;
527 r = MSI_DatabaseOpenViewW( db, query, &view );
528 if (r != ERROR_SUCCESS)
529 return ERROR_SUCCESS;
531 rec = MSI_CreateRecord( 2 );
532 MSI_RecordSetInteger( rec, 1, pos->min );
533 MSI_RecordSetInteger( rec, 2, pos->max );
535 r = MSI_ViewExecute( view, rec );
536 msiobj_release( &rec->hdr );
537 if (r != ERROR_SUCCESS)
538 goto done;
540 LIST_FOR_EACH_ENTRY( po, &pos->files, struct patch_offset, entry )
542 UINT r_fetch;
543 while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
545 const WCHAR *file = MSI_RecordGetString( rec, 1 );
546 UINT seq;
548 if (!strcmpiW( file, po->name ))
550 /* update record */
551 seq = MSI_RecordGetInteger( rec, 8 );
552 MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply );
553 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
554 if (r != ERROR_SUCCESS)
555 ERR("Failed to update offset for file %s\n", debugstr_w(file));
556 msiobj_release( &rec->hdr );
557 break;
559 msiobj_release( &rec->hdr );
561 if (r_fetch != ERROR_SUCCESS) break;
564 done:
565 msiobj_release( &view->hdr );
566 return ERROR_SUCCESS;
569 static const WCHAR patch_media_query[] = {
570 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
571 'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
572 'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
573 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
575 struct patch_media
577 struct list entry;
578 UINT disk_id;
579 UINT last_sequence;
580 WCHAR *prompt;
581 WCHAR *cabinet;
582 WCHAR *volume;
583 WCHAR *source;
586 static UINT add_patch_media( MSIPACKAGE *package, IStorage *patch )
588 static const WCHAR delete_query[] = {
589 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
590 'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0};
591 static const WCHAR insert_query[] = {
592 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ',
593 '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',',
594 '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',',
595 '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ',
596 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
597 MSIQUERY *view;
598 MSIRECORD *rec;
599 UINT r, disk_id;
600 struct list media_list;
601 struct patch_media *media, *next;
603 r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view );
604 if (r != ERROR_SUCCESS) return r;
606 r = MSI_ViewExecute( view, 0 );
607 if (r != ERROR_SUCCESS)
609 msiobj_release( &view->hdr );
610 TRACE("query failed %u\n", r);
611 return r;
613 list_init( &media_list );
614 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
616 disk_id = MSI_RecordGetInteger( rec, 1 );
617 TRACE("disk_id %u\n", disk_id);
618 if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
620 msiobj_release( &rec->hdr );
621 continue;
623 if (!(media = msi_alloc( sizeof( *media )))) goto done;
624 media->disk_id = disk_id;
625 media->last_sequence = MSI_RecordGetInteger( rec, 2 );
626 media->prompt = msi_dup_record_field( rec, 3 );
627 media->cabinet = msi_dup_record_field( rec, 4 );
628 media->volume = msi_dup_record_field( rec, 5 );
629 media->source = msi_dup_record_field( rec, 6 );
631 list_add_tail( &media_list, &media->entry );
632 msiobj_release( &rec->hdr );
634 LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry )
636 MSIQUERY *delete_view, *insert_view;
638 r = MSI_DatabaseOpenViewW( package->db, delete_query, &delete_view );
639 if (r != ERROR_SUCCESS) goto done;
641 rec = MSI_CreateRecord( 1 );
642 MSI_RecordSetInteger( rec, 1, media->disk_id );
644 r = MSI_ViewExecute( delete_view, rec );
645 msiobj_release( &delete_view->hdr );
646 msiobj_release( &rec->hdr );
647 if (r != ERROR_SUCCESS) goto done;
649 r = MSI_DatabaseOpenViewW( package->db, insert_query, &insert_view );
650 if (r != ERROR_SUCCESS) goto done;
652 disk_id = package->db->media_transform_disk_id;
653 TRACE("disk id %u\n", disk_id);
654 TRACE("last sequence %u\n", media->last_sequence);
655 TRACE("prompt %s\n", debugstr_w(media->prompt));
656 TRACE("cabinet %s\n", debugstr_w(media->cabinet));
657 TRACE("volume %s\n", debugstr_w(media->volume));
658 TRACE("source %s\n", debugstr_w(media->source));
660 rec = MSI_CreateRecord( 6 );
661 MSI_RecordSetInteger( rec, 1, disk_id );
662 MSI_RecordSetInteger( rec, 2, media->last_sequence );
663 MSI_RecordSetStringW( rec, 3, media->prompt );
664 MSI_RecordSetStringW( rec, 4, media->cabinet );
665 MSI_RecordSetStringW( rec, 5, media->volume );
666 MSI_RecordSetStringW( rec, 6, media->source );
668 r = MSI_ViewExecute( insert_view, rec );
669 msiobj_release( &insert_view->hdr );
670 msiobj_release( &rec->hdr );
671 if (r != ERROR_SUCCESS) goto done;
673 r = msi_add_cabinet_stream( package, disk_id, patch, media->cabinet );
674 if (r != ERROR_SUCCESS) WARN("failed to add cabinet stream %u\n", r);
675 package->db->media_transform_disk_id++;
678 done:
679 msiobj_release( &view->hdr );
680 LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry )
682 list_remove( &media->entry );
683 msi_free( media->prompt );
684 msi_free( media->cabinet );
685 msi_free( media->volume );
686 msi_free( media->source );
687 msi_free( media );
689 return r;
692 static UINT set_patch_offsets( MSIDATABASE *db )
694 MSIQUERY *view;
695 MSIRECORD *rec;
696 UINT r;
698 r = MSI_DatabaseOpenViewW( db, patch_media_query, &view );
699 if (r != ERROR_SUCCESS)
700 return r;
702 r = MSI_ViewExecute( view, 0 );
703 if (r != ERROR_SUCCESS)
704 goto done;
706 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
708 UINT last_sequence = MSI_RecordGetInteger( rec, 2 );
709 struct patch_offset_list *pos;
711 /* FIXME: set/check Source field instead? */
712 if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
714 msiobj_release( &rec->hdr );
715 continue;
717 pos = patch_offset_list_create();
718 patch_offset_get_files( db, last_sequence, pos );
719 patch_offset_get_patches( db, last_sequence, pos );
721 UINT offset = db->media_transform_offset - pos->min;
722 last_sequence = offset + pos->max;
724 /* FIXME: this is for the patch table, which is not yet properly transformed */
725 last_sequence += pos->min;
726 pos->offset_to_apply = offset;
727 if (pos->count)
728 patch_offset_modify_db( db, pos );
730 MSI_RecordSetInteger( rec, 2, last_sequence );
731 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
732 if (r != ERROR_SUCCESS)
733 ERR("Failed to update Media table entry, expect breakage (%u)\n", r);
734 db->media_transform_offset = last_sequence + 1;
736 patch_offset_list_free( pos );
737 msiobj_release( &rec->hdr );
740 done:
741 msiobj_release( &view->hdr );
742 return r;
745 static UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
747 UINT i, r = ERROR_SUCCESS;
748 WCHAR **substorage;
750 /* apply substorage transforms */
751 substorage = msi_split_string( patch->transforms, ';' );
752 for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
754 r = apply_substorage_transform( package, patch_db, substorage[i] );
755 if (r == ERROR_SUCCESS)
757 add_patch_media( package, patch_db->storage );
758 set_patch_offsets( package->db );
761 msi_free( substorage );
762 if (r != ERROR_SUCCESS)
763 return r;
765 patch_set_media_source_prop( package );
767 patch->state = MSIPATCHSTATE_APPLIED;
768 list_add_tail( &package->patches, &patch->entry );
769 return ERROR_SUCCESS;
772 void msi_free_patchinfo( MSIPATCHINFO *patch )
774 msi_free( patch->patchcode );
775 msi_free( patch->products );
776 msi_free( patch->transforms );
777 msi_free( patch->filename );
778 msi_free( patch->localfile );
779 msi_free( patch );
782 static UINT msi_apply_patch_package( MSIPACKAGE *package, const WCHAR *file )
784 static const WCHAR dotmsp[] = {'.','m','s','p',0};
785 MSIDATABASE *patch_db = NULL;
786 WCHAR localfile[MAX_PATH];
787 MSISUMMARYINFO *si;
788 MSIPATCHINFO *patch = NULL;
789 UINT r = ERROR_SUCCESS;
791 TRACE("%p %s\n", package, debugstr_w(file));
793 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
794 if (r != ERROR_SUCCESS)
796 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
797 return r;
799 if (!(si = MSI_GetSummaryInformationW( patch_db->storage, 0 )))
801 msiobj_release( &patch_db->hdr );
802 return ERROR_FUNCTION_FAILED;
804 r = msi_check_patch_applicable( package, si );
805 if (r != ERROR_SUCCESS)
807 TRACE("patch not applicable\n");
808 r = ERROR_SUCCESS;
809 goto done;
811 r = msi_parse_patch_summary( si, &patch );
812 if ( r != ERROR_SUCCESS )
813 goto done;
815 r = msi_create_empty_local_file( localfile, dotmsp );
816 if ( r != ERROR_SUCCESS )
817 goto done;
819 r = ERROR_OUTOFMEMORY;
820 if (!(patch->filename = strdupW( file ))) goto done;
821 if (!(patch->localfile = strdupW( localfile ))) goto done;
823 r = msi_apply_patch_db( package, patch_db, patch );
824 if (r != ERROR_SUCCESS) WARN("patch failed to apply %u\n", r);
826 done:
827 msiobj_release( &si->hdr );
828 msiobj_release( &patch_db->hdr );
829 if (patch && r != ERROR_SUCCESS)
831 DeleteFileW( patch->localfile );
832 msi_free_patchinfo( patch );
834 return r;
837 /* get the PATCH property, and apply all the patches it specifies */
838 UINT msi_apply_patches( MSIPACKAGE *package )
840 LPWSTR patch_list, *patches;
841 UINT i, r = ERROR_SUCCESS;
843 patch_list = msi_dup_property( package->db, szPatch );
845 TRACE("patches to be applied: %s\n", debugstr_w(patch_list));
847 patches = msi_split_string( patch_list, ';' );
848 for (i = 0; patches && patches[i] && r == ERROR_SUCCESS; i++)
849 r = msi_apply_patch_package( package, patches[i] );
851 msi_free( patches );
852 msi_free( patch_list );
853 return r;
856 UINT msi_apply_transforms( MSIPACKAGE *package )
858 static const WCHAR szTransforms[] = {'T','R','A','N','S','F','O','R','M','S',0};
859 LPWSTR xform_list, *xforms;
860 UINT i, r = ERROR_SUCCESS;
862 xform_list = msi_dup_property( package->db, szTransforms );
863 xforms = msi_split_string( xform_list, ';' );
865 for (i = 0; xforms && xforms[i] && r == ERROR_SUCCESS; i++)
867 if (xforms[i][0] == ':')
868 r = apply_substorage_transform( package, package->db, xforms[i] );
869 else
871 WCHAR *transform;
873 if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
874 else
876 WCHAR *p = strrchrW( package->PackagePath, '\\' );
877 DWORD len = p - package->PackagePath + 1;
879 if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
881 msi_free( xforms );
882 msi_free( xform_list );
883 return ERROR_OUTOFMEMORY;
885 memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
886 memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
888 r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
889 if (transform != xforms[i]) msi_free( transform );
892 msi_free( xforms );
893 msi_free( xform_list );
894 return r;
897 UINT msi_apply_registered_patch( MSIPACKAGE *package, LPCWSTR patch_code )
899 UINT r;
900 DWORD len;
901 WCHAR patch_file[MAX_PATH];
902 MSIDATABASE *patch_db;
903 MSIPATCHINFO *patch_info;
904 MSISUMMARYINFO *si;
906 len = sizeof(patch_file) / sizeof(WCHAR);
907 r = MsiGetPatchInfoExW( patch_code, package->ProductCode, NULL, package->Context,
908 INSTALLPROPERTY_LOCALPACKAGEW, patch_file, &len );
909 if (r != ERROR_SUCCESS)
911 ERR("failed to get patch filename %u\n", r);
912 return r;
914 r = MSI_OpenDatabaseW( patch_file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
915 if (r != ERROR_SUCCESS)
917 ERR("failed to open patch database %s\n", debugstr_w( patch_file ));
918 return r;
920 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
921 if (!si)
923 msiobj_release( &patch_db->hdr );
924 return ERROR_FUNCTION_FAILED;
926 r = msi_parse_patch_summary( si, &patch_info );
927 msiobj_release( &si->hdr );
928 if (r != ERROR_SUCCESS)
930 ERR("failed to parse patch summary %u\n", r);
931 msiobj_release( &patch_db->hdr );
932 return r;
934 patch_info->localfile = strdupW( patch_file );
935 if (!patch_info->localfile)
937 msiobj_release( &patch_db->hdr );
938 msi_free_patchinfo( patch_info );
939 return ERROR_OUTOFMEMORY;
941 r = msi_apply_patch_db( package, patch_db, patch_info );
942 msiobj_release( &patch_db->hdr );
943 if (r != ERROR_SUCCESS)
945 ERR("failed to apply patch %u\n", r);
946 msi_free_patchinfo( patch_info );
948 return r;