vcomp/tests: Add tests for atomic double functions.
[wine.git] / dlls / msi / patch.c
blobf8f3eaf5a2ee97abb43c8d7a7a2494ffdc17fa17
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;
128 UINT r, valid_flags = 0, wanted_flags = 0;
129 WCHAR *template, *product, *p;
130 struct transform_desc *desc;
132 r = msi_get_suminfo( transform, 0, &si );
133 if (r != ERROR_SUCCESS)
135 WARN("no summary information!\n");
136 return r;
138 wanted_flags = msi_suminfo_get_int32( si, PID_CHARCOUNT );
139 wanted_flags &= 0xffff; /* mask off error condition flags */
140 TRACE("validation flags 0x%04x\n", wanted_flags);
142 if (wanted_flags & ~supported_flags)
144 FIXME("unsupported validation flags 0x%04x\n", wanted_flags);
145 msiobj_release( &si->hdr );
146 return ERROR_FUNCTION_FAILED;
148 if (!(template = msi_suminfo_dup_string( si, PID_TEMPLATE )))
150 WARN("no template property!\n");
151 msiobj_release( &si->hdr );
152 return ERROR_FUNCTION_FAILED;
154 TRACE("template property: %s\n", debugstr_w(template));
155 if (!(product = msi_get_suminfo_product( transform )))
157 WARN("no product property!\n");
158 msi_free( template );
159 msiobj_release( &si->hdr );
160 return ERROR_FUNCTION_FAILED;
162 TRACE("product property: %s\n", debugstr_w(product));
163 if (!(desc = parse_transform_desc( product )))
165 msi_free( template );
166 msiobj_release( &si->hdr );
167 return ERROR_FUNCTION_FAILED;
169 msi_free( product );
171 if (wanted_flags & MSITRANSFORM_VALIDATE_LANGUAGE)
173 if (!template[0] || ((p = strchrW( template, ';' )) && match_language( package, atoiW( p + 1 ) )))
175 valid_flags |= MSITRANSFORM_VALIDATE_LANGUAGE;
178 if (wanted_flags & MSITRANSFORM_VALIDATE_PRODUCT)
180 WCHAR *product_code_installed = msi_dup_property( package->db, szProductCode );
182 if (!product_code_installed)
184 msi_free( template );
185 free_transform_desc( desc );
186 msiobj_release( &si->hdr );
187 return ERROR_INSTALL_PACKAGE_INVALID;
189 if (!strcmpW( desc->product_code_from, product_code_installed ))
191 valid_flags |= MSITRANSFORM_VALIDATE_PRODUCT;
193 msi_free( product_code_installed );
195 if (wanted_flags & MSITRANSFORM_VALIDATE_PLATFORM)
197 if ((p = strchrW( template, ';' )))
199 *p = 0;
200 if (package->platform == parse_platform( template ))
201 valid_flags |= MSITRANSFORM_VALIDATE_PLATFORM;
204 msi_free( template );
205 if (wanted_flags & MSITRANSFORM_VALIDATE_MAJORVERSION)
207 WCHAR *product_version_installed = msi_dup_property( package->db, szProductVersion );
208 DWORD major_installed, minor_installed, major, minor;
210 if (!product_version_installed)
212 free_transform_desc( desc );
213 msiobj_release( &si->hdr );
214 return ERROR_INSTALL_PACKAGE_INVALID;
216 msi_parse_version_string( product_version_installed, &major_installed, &minor_installed );
217 msi_parse_version_string( desc->version_from, &major, &minor );
219 if (major_installed == major)
221 valid_flags |= MSITRANSFORM_VALIDATE_MAJORVERSION;
222 wanted_flags &= ~MSITRANSFORM_VALIDATE_MINORVERSION;
224 msi_free( product_version_installed );
226 else if (wanted_flags & MSITRANSFORM_VALIDATE_MINORVERSION)
228 WCHAR *product_version_installed = msi_dup_property( package->db, szProductVersion );
229 DWORD major_installed, minor_installed, major, minor;
231 if (!product_version_installed)
233 free_transform_desc( desc );
234 msiobj_release( &si->hdr );
235 return ERROR_INSTALL_PACKAGE_INVALID;
237 msi_parse_version_string( product_version_installed, &major_installed, &minor_installed );
238 msi_parse_version_string( desc->version_from, &major, &minor );
240 if (major_installed == major && minor_installed == minor)
241 valid_flags |= MSITRANSFORM_VALIDATE_MINORVERSION;
242 msi_free( product_version_installed );
244 if (wanted_flags & MSITRANSFORM_VALIDATE_UPGRADECODE)
246 WCHAR *upgrade_code_installed = msi_dup_property( package->db, szUpgradeCode );
248 if (!upgrade_code_installed)
250 free_transform_desc( desc );
251 msiobj_release( &si->hdr );
252 return ERROR_INSTALL_PACKAGE_INVALID;
254 if (!strcmpW( desc->upgrade_code, upgrade_code_installed ))
255 valid_flags |= MSITRANSFORM_VALIDATE_UPGRADECODE;
256 msi_free( upgrade_code_installed );
259 free_transform_desc( desc );
260 msiobj_release( &si->hdr );
261 if ((valid_flags & wanted_flags) != wanted_flags) return ERROR_FUNCTION_FAILED;
262 TRACE("applicable transform\n");
263 return ERROR_SUCCESS;
266 static UINT apply_substorage_transform( MSIPACKAGE *package, MSIDATABASE *patch_db, LPCWSTR name )
268 UINT ret = ERROR_FUNCTION_FAILED;
269 IStorage *stg = NULL;
270 HRESULT r;
272 TRACE("%p %s\n", package, debugstr_w(name));
274 if (*name++ != ':')
276 ERR("expected a colon in %s\n", debugstr_w(name));
277 return ERROR_FUNCTION_FAILED;
279 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
280 if (SUCCEEDED(r))
282 ret = check_transform_applicable( package, stg );
283 if (ret == ERROR_SUCCESS)
284 msi_table_apply_transform( package->db, stg );
285 else
286 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
287 IStorage_Release( stg );
289 else
291 ERR("failed to open substorage %s\n", debugstr_w(name));
293 return ERROR_SUCCESS;
296 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
298 LPWSTR guid_list, *guids, product_code;
299 UINT i, ret = ERROR_FUNCTION_FAILED;
301 product_code = msi_dup_property( package->db, szProductCode );
302 if (!product_code)
304 /* FIXME: the property ProductCode should be written into the DB somewhere */
305 ERR("no product code to check\n");
306 return ERROR_SUCCESS;
308 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
309 guids = msi_split_string( guid_list, ';' );
310 for (i = 0; guids[i] && ret != ERROR_SUCCESS; i++)
312 if (!strcmpW( guids[i], product_code )) ret = ERROR_SUCCESS;
314 msi_free( guids );
315 msi_free( guid_list );
316 msi_free( product_code );
317 return ret;
320 static UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
322 MSIPATCHINFO *pi;
323 UINT r = ERROR_SUCCESS;
324 WCHAR *p;
326 if (!(pi = msi_alloc_zero( sizeof(MSIPATCHINFO) )))
328 return ERROR_OUTOFMEMORY;
330 if (!(pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER )))
332 msi_free( pi );
333 return ERROR_OUTOFMEMORY;
335 p = pi->patchcode;
336 if (*p != '{')
338 msi_free( pi->patchcode );
339 msi_free( pi );
340 return ERROR_PATCH_PACKAGE_INVALID;
342 if (!(p = strchrW( p + 1, '}' )))
344 msi_free( pi->patchcode );
345 msi_free( pi );
346 return ERROR_PATCH_PACKAGE_INVALID;
348 if (p[1])
350 FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
351 p[1] = 0;
353 TRACE("patch code %s\n", debugstr_w(pi->patchcode));
354 if (!(pi->products = msi_suminfo_dup_string( si, PID_TEMPLATE )))
356 msi_free( pi->patchcode );
357 msi_free( pi );
358 return ERROR_OUTOFMEMORY;
360 if (!(pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR )))
362 msi_free( pi->patchcode );
363 msi_free( pi->products );
364 msi_free( pi );
365 return ERROR_OUTOFMEMORY;
367 *patch = pi;
368 return r;
371 static UINT patch_set_media_source_prop( MSIPACKAGE *package )
373 static const WCHAR query[] = {
374 'S','E','L','E','C','T',' ','`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
375 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ',
376 'I','S',' ','N','O','T',' ','N','U','L','L',0};
377 MSIQUERY *view;
378 MSIRECORD *rec;
379 const WCHAR *property;
380 WCHAR *patch;
381 UINT r;
383 r = MSI_DatabaseOpenViewW( package->db, query, &view );
384 if (r != ERROR_SUCCESS)
385 return r;
387 r = MSI_ViewExecute( view, 0 );
388 if (r != ERROR_SUCCESS)
389 goto done;
391 if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
393 property = MSI_RecordGetString( rec, 1 );
394 patch = msi_dup_property( package->db, szPatch );
395 msi_set_property( package->db, property, patch, -1 );
396 msi_free( patch );
397 msiobj_release( &rec->hdr );
400 done:
401 msiobj_release( &view->hdr );
402 return r;
405 struct patch_offset
407 struct list entry;
408 WCHAR *name;
409 UINT sequence;
412 struct patch_offset_list
414 struct list files;
415 struct list patches;
416 UINT count, min, max;
417 UINT offset_to_apply;
420 static struct patch_offset_list *patch_offset_list_create( void )
422 struct patch_offset_list *pos = msi_alloc( sizeof(struct patch_offset_list) );
423 list_init( &pos->files );
424 list_init( &pos->patches );
425 pos->count = pos->max = 0;
426 pos->min = 999999;
427 return pos;
430 static void patch_offset_list_free( struct patch_offset_list *pos )
432 struct patch_offset *po, *po2;
434 LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct patch_offset, entry )
436 msi_free( po->name );
437 msi_free( po );
439 LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->patches, struct patch_offset, entry )
441 msi_free( po->name );
442 msi_free( po );
444 msi_free( pos );
447 static void patch_offset_get_filepatches( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
449 static const WCHAR query[] = {
450 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
451 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
452 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
453 MSIQUERY *view;
454 MSIRECORD *rec;
455 UINT r;
457 r = MSI_DatabaseOpenViewW( db, query, &view );
458 if (r != ERROR_SUCCESS)
459 return;
461 rec = MSI_CreateRecord( 1 );
462 MSI_RecordSetInteger( rec, 1, last_sequence );
464 r = MSI_ViewExecute( view, rec );
465 msiobj_release( &rec->hdr );
466 if (r != ERROR_SUCCESS)
467 return;
469 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
471 struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) );
473 po->name = msi_dup_record_field( rec, 1 );
474 po->sequence = MSI_RecordGetInteger( rec, 2 );
475 pos->min = min( pos->min, po->sequence );
476 pos->max = max( pos->max, po->sequence );
477 list_add_tail( &pos->patches, &po->entry );
478 pos->count++;
480 msiobj_release( &rec->hdr );
482 msiobj_release( &view->hdr );
485 static void patch_offset_get_files( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
487 static const WCHAR query[] = {
488 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
489 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
490 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
491 MSIQUERY *view;
492 MSIRECORD *rec;
493 UINT r;
495 r = MSI_DatabaseOpenViewW( db, query, &view );
496 if (r != ERROR_SUCCESS)
497 return;
499 rec = MSI_CreateRecord( 1 );
500 MSI_RecordSetInteger( rec, 1, last_sequence );
502 r = MSI_ViewExecute( view, rec );
503 msiobj_release( &rec->hdr );
504 if (r != ERROR_SUCCESS)
505 return;
507 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
509 UINT attributes = MSI_RecordGetInteger( rec, 7 );
510 if (attributes & msidbFileAttributesPatchAdded)
512 struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) );
514 po->name = msi_dup_record_field( rec, 1 );
515 po->sequence = MSI_RecordGetInteger( rec, 8 );
516 pos->min = min( pos->min, po->sequence );
517 pos->max = max( pos->max, po->sequence );
518 list_add_tail( &pos->files, &po->entry );
519 pos->count++;
521 msiobj_release( &rec->hdr );
523 msiobj_release( &view->hdr );
526 static UINT patch_update_file_sequence( MSIDATABASE *db, const struct patch_offset_list *pos,
527 MSIQUERY *view, MSIRECORD *rec )
529 struct patch_offset *po;
530 const WCHAR *file = MSI_RecordGetString( rec, 1 );
531 UINT r = ERROR_SUCCESS, seq = MSI_RecordGetInteger( rec, 8 );
533 LIST_FOR_EACH_ENTRY( po, &pos->files, struct patch_offset, entry )
535 if (!strcmpiW( file, po->name ))
537 MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply );
538 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
539 if (r != ERROR_SUCCESS)
540 ERR("Failed to update offset for file %s (%u)\n", debugstr_w(file), r);
541 break;
544 return r;
547 static UINT patch_update_filepatch_sequence( MSIDATABASE *db, const struct patch_offset_list *pos,
548 MSIQUERY *view, MSIRECORD *rec )
550 static const WCHAR delete_query[] = {
551 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','P','a','t','c','h','`',' ',
552 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','?',' ',
553 'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','=',' ','?',0};
554 static const WCHAR insert_query[] = {
555 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','P','a','t','c','h','`',' ',
556 '(','`','F','i','l','e','_','`',',','`','S','e','q','u','e','n','c','e','`',',',
557 '`','P','a','t','c','h','S','i','z','e','`',',','`','A','t','t','r','i','b','u','t','e','s','`',',',
558 '`','H','e','a','d','e','r','`',',','`','S','t','r','e','a','m','R','e','f','_','`',')',' ',
559 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
560 struct patch_offset *po;
561 const WCHAR *file = MSI_RecordGetString( rec, 1 );
562 UINT r = ERROR_SUCCESS, seq = MSI_RecordGetInteger( rec, 2 );
564 LIST_FOR_EACH_ENTRY( po, &pos->patches, struct patch_offset, entry )
566 if (seq == po->sequence && !strcmpiW( file, po->name ))
568 MSIQUERY *delete_view, *insert_view;
569 MSIRECORD *rec2;
571 r = MSI_DatabaseOpenViewW( db, delete_query, &delete_view );
572 if (r != ERROR_SUCCESS) return r;
574 rec2 = MSI_CreateRecord( 2 );
575 MSI_RecordSetStringW( rec2, 1, po->name );
576 MSI_RecordSetInteger( rec2, 2, po->sequence );
577 r = MSI_ViewExecute( delete_view, rec2 );
578 msiobj_release( &delete_view->hdr );
579 msiobj_release( &rec2->hdr );
580 if (r != ERROR_SUCCESS) return r;
582 r = MSI_DatabaseOpenViewW( db, insert_query, &insert_view );
583 if (r != ERROR_SUCCESS) return r;
585 MSI_RecordSetInteger( rec, 2, po->sequence + pos->offset_to_apply );
587 r = MSI_ViewExecute( insert_view, rec );
588 msiobj_release( &insert_view->hdr );
589 if (r != ERROR_SUCCESS)
590 ERR("Failed to update offset for filepatch %s (%u)\n", debugstr_w(file), r);
591 break;
594 return r;
597 static UINT patch_offset_modify_db( MSIDATABASE *db, struct patch_offset_list *pos )
599 static const WCHAR file_query[] = {
600 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','i','l','e','`',' ',
601 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>','=',' ','?',' ',
602 'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','<','=',' ','?',' ',
603 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
604 static const WCHAR patch_query[] = {
605 'S','E','L','E','C','T',' ','*','F','R','O','M',' ','`','P','a','t','c','h','`',' ',
606 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>','=',' ','?',' ',
607 'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','<','=',' ','?',' ',
608 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
609 MSIRECORD *rec;
610 MSIQUERY *view;
611 UINT r, min = pos->min, max = pos->max, r_fetch;
613 r = MSI_DatabaseOpenViewW( db, file_query, &view );
614 if (r != ERROR_SUCCESS)
615 return ERROR_SUCCESS;
617 rec = MSI_CreateRecord( 2 );
618 MSI_RecordSetInteger( rec, 1, min );
619 MSI_RecordSetInteger( rec, 2, max );
621 r = MSI_ViewExecute( view, rec );
622 msiobj_release( &rec->hdr );
623 if (r != ERROR_SUCCESS)
624 goto done;
626 while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
628 r = patch_update_file_sequence( db, pos, view, rec );
629 msiobj_release( &rec->hdr );
630 if (r != ERROR_SUCCESS) goto done;
632 msiobj_release( &view->hdr );
634 r = MSI_DatabaseOpenViewW( db, patch_query, &view );
635 if (r != ERROR_SUCCESS)
636 return ERROR_SUCCESS;
638 rec = MSI_CreateRecord( 2 );
639 MSI_RecordSetInteger( rec, 1, min );
640 MSI_RecordSetInteger( rec, 2, max );
642 r = MSI_ViewExecute( view, rec );
643 msiobj_release( &rec->hdr );
644 if (r != ERROR_SUCCESS)
645 goto done;
647 while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
649 r = patch_update_filepatch_sequence( db, pos, view, rec );
650 msiobj_release( &rec->hdr );
651 if (r != ERROR_SUCCESS) goto done;
654 done:
655 msiobj_release( &view->hdr );
656 return r;
659 static const WCHAR patch_media_query[] = {
660 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
661 'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
662 'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
663 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
665 struct patch_media
667 struct list entry;
668 UINT disk_id;
669 UINT last_sequence;
670 WCHAR *prompt;
671 WCHAR *cabinet;
672 WCHAR *volume;
673 WCHAR *source;
676 static UINT patch_add_media( MSIPACKAGE *package, IStorage *storage, MSIPATCHINFO *patch )
678 static const WCHAR delete_query[] = {
679 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
680 'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0};
681 static const WCHAR insert_query[] = {
682 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ',
683 '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',',
684 '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',',
685 '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ',
686 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
687 MSIQUERY *view;
688 MSIRECORD *rec;
689 UINT r, disk_id;
690 struct list media_list;
691 struct patch_media *media, *next;
693 r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view );
694 if (r != ERROR_SUCCESS) return r;
696 r = MSI_ViewExecute( view, 0 );
697 if (r != ERROR_SUCCESS)
699 msiobj_release( &view->hdr );
700 TRACE("query failed %u\n", r);
701 return r;
703 list_init( &media_list );
704 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
706 disk_id = MSI_RecordGetInteger( rec, 1 );
707 TRACE("disk_id %u\n", disk_id);
708 if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
710 msiobj_release( &rec->hdr );
711 continue;
713 if (!(media = msi_alloc( sizeof( *media )))) {
714 msiobj_release( &rec->hdr );
715 goto done;
717 media->disk_id = disk_id;
718 media->last_sequence = MSI_RecordGetInteger( rec, 2 );
719 media->prompt = msi_dup_record_field( rec, 3 );
720 media->cabinet = msi_dup_record_field( rec, 4 );
721 media->volume = msi_dup_record_field( rec, 5 );
722 media->source = msi_dup_record_field( rec, 6 );
724 list_add_tail( &media_list, &media->entry );
725 msiobj_release( &rec->hdr );
727 LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry )
729 MSIQUERY *delete_view, *insert_view;
731 r = MSI_DatabaseOpenViewW( package->db, delete_query, &delete_view );
732 if (r != ERROR_SUCCESS) goto done;
734 rec = MSI_CreateRecord( 1 );
735 MSI_RecordSetInteger( rec, 1, media->disk_id );
737 r = MSI_ViewExecute( delete_view, rec );
738 msiobj_release( &delete_view->hdr );
739 msiobj_release( &rec->hdr );
740 if (r != ERROR_SUCCESS) goto done;
742 r = MSI_DatabaseOpenViewW( package->db, insert_query, &insert_view );
743 if (r != ERROR_SUCCESS) goto done;
745 disk_id = package->db->media_transform_disk_id;
746 TRACE("disk id %u\n", disk_id);
747 TRACE("last sequence %u\n", media->last_sequence);
748 TRACE("prompt %s\n", debugstr_w(media->prompt));
749 TRACE("cabinet %s\n", debugstr_w(media->cabinet));
750 TRACE("volume %s\n", debugstr_w(media->volume));
751 TRACE("source %s\n", debugstr_w(media->source));
753 rec = MSI_CreateRecord( 6 );
754 MSI_RecordSetInteger( rec, 1, disk_id );
755 MSI_RecordSetInteger( rec, 2, media->last_sequence );
756 MSI_RecordSetStringW( rec, 3, media->prompt );
757 MSI_RecordSetStringW( rec, 4, media->cabinet );
758 MSI_RecordSetStringW( rec, 5, media->volume );
759 MSI_RecordSetStringW( rec, 6, media->source );
761 r = MSI_ViewExecute( insert_view, rec );
762 msiobj_release( &insert_view->hdr );
763 msiobj_release( &rec->hdr );
764 if (r != ERROR_SUCCESS) goto done;
766 r = msi_add_cabinet_stream( package, disk_id, storage, media->cabinet );
767 if (r != ERROR_SUCCESS) ERR("failed to add cabinet stream %u\n", r);
768 else
770 patch->disk_id = disk_id;
771 package->db->media_transform_disk_id++;
775 done:
776 msiobj_release( &view->hdr );
777 LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry )
779 list_remove( &media->entry );
780 msi_free( media->prompt );
781 msi_free( media->cabinet );
782 msi_free( media->volume );
783 msi_free( media->source );
784 msi_free( media );
786 return r;
789 static UINT patch_set_offsets( MSIDATABASE *db, MSIPATCHINFO *patch )
791 MSIQUERY *view;
792 MSIRECORD *rec;
793 UINT r;
795 r = MSI_DatabaseOpenViewW( db, patch_media_query, &view );
796 if (r != ERROR_SUCCESS)
797 return r;
799 r = MSI_ViewExecute( view, 0 );
800 if (r != ERROR_SUCCESS)
801 goto done;
803 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
805 UINT offset, last_sequence = MSI_RecordGetInteger( rec, 2 );
806 struct patch_offset_list *pos;
808 /* FIXME: set/check Source field instead? */
809 if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
811 msiobj_release( &rec->hdr );
812 continue;
814 pos = patch_offset_list_create();
815 patch_offset_get_files( db, last_sequence, pos );
816 patch_offset_get_filepatches( db, last_sequence, pos );
818 offset = db->media_transform_offset - pos->min;
819 last_sequence = offset + pos->max;
821 last_sequence += pos->min;
822 pos->offset_to_apply = offset;
823 if (pos->count)
825 r = patch_offset_modify_db( db, pos );
826 if (r != ERROR_SUCCESS)
827 ERR("Failed to set offsets, expect breakage (%u)\n", r);
829 MSI_RecordSetInteger( rec, 2, last_sequence );
830 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
831 if (r != ERROR_SUCCESS)
832 ERR("Failed to update Media table entry, expect breakage (%u)\n", r);
834 db->media_transform_offset = last_sequence + 1;
836 patch_offset_list_free( pos );
837 msiobj_release( &rec->hdr );
840 done:
841 msiobj_release( &view->hdr );
842 return r;
845 static UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
847 UINT i, r = ERROR_SUCCESS;
848 WCHAR **substorage;
850 /* apply substorage transforms */
851 substorage = msi_split_string( patch->transforms, ';' );
852 for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
854 r = apply_substorage_transform( package, patch_db, substorage[i] );
855 if (r == ERROR_SUCCESS)
857 r = patch_set_offsets( package->db, patch );
858 if (r == ERROR_SUCCESS)
859 r = patch_add_media( package, patch_db->storage, patch );
862 msi_free( substorage );
863 if (r != ERROR_SUCCESS)
864 return r;
866 r = patch_set_media_source_prop( package );
867 if (r != ERROR_SUCCESS)
868 return r;
870 patch->state = MSIPATCHSTATE_APPLIED;
871 list_add_tail( &package->patches, &patch->entry );
872 return ERROR_SUCCESS;
875 void msi_free_patchinfo( MSIPATCHINFO *patch )
877 msi_free( patch->patchcode );
878 msi_free( patch->products );
879 msi_free( patch->transforms );
880 msi_free( patch->filename );
881 msi_free( patch->localfile );
882 msi_free( patch );
885 static UINT msi_apply_patch_package( MSIPACKAGE *package, const WCHAR *file )
887 static const WCHAR dotmsp[] = {'.','m','s','p',0};
888 MSIDATABASE *patch_db = NULL;
889 WCHAR localfile[MAX_PATH];
890 MSISUMMARYINFO *si;
891 MSIPATCHINFO *patch = NULL;
892 UINT r;
894 TRACE("%p, %s\n", package, debugstr_w(file));
896 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
897 if (r != ERROR_SUCCESS)
899 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
900 return r;
902 r = msi_get_suminfo( patch_db->storage, 0, &si );
903 if (r != ERROR_SUCCESS)
905 msiobj_release( &patch_db->hdr );
906 return r;
908 r = msi_check_patch_applicable( package, si );
909 if (r != ERROR_SUCCESS)
911 TRACE("patch not applicable\n");
912 r = ERROR_SUCCESS;
913 goto done;
915 r = msi_parse_patch_summary( si, &patch );
916 if ( r != ERROR_SUCCESS )
917 goto done;
919 r = msi_create_empty_local_file( localfile, dotmsp );
920 if ( r != ERROR_SUCCESS )
921 goto done;
923 r = ERROR_OUTOFMEMORY;
924 patch->registered = FALSE;
925 if (!(patch->filename = strdupW( file ))) goto done;
926 if (!(patch->localfile = strdupW( localfile ))) goto done;
928 r = msi_apply_patch_db( package, patch_db, patch );
929 if (r != ERROR_SUCCESS) WARN("patch failed to apply %u\n", r);
931 done:
932 msiobj_release( &si->hdr );
933 msiobj_release( &patch_db->hdr );
934 if (patch && r != ERROR_SUCCESS)
936 DeleteFileW( patch->localfile );
937 msi_free_patchinfo( patch );
939 return r;
942 /* get the PATCH property, and apply all the patches it specifies */
943 UINT msi_apply_patches( MSIPACKAGE *package )
945 LPWSTR patch_list, *patches;
946 UINT i, r = ERROR_SUCCESS;
948 patch_list = msi_dup_property( package->db, szPatch );
950 TRACE("patches to be applied: %s\n", debugstr_w(patch_list));
952 patches = msi_split_string( patch_list, ';' );
953 for (i = 0; patches && patches[i] && r == ERROR_SUCCESS; i++)
954 r = msi_apply_patch_package( package, patches[i] );
956 msi_free( patches );
957 msi_free( patch_list );
958 return r;
961 UINT msi_apply_transforms( MSIPACKAGE *package )
963 static const WCHAR szTransforms[] = {'T','R','A','N','S','F','O','R','M','S',0};
964 LPWSTR xform_list, *xforms;
965 UINT i, r = ERROR_SUCCESS;
967 xform_list = msi_dup_property( package->db, szTransforms );
968 xforms = msi_split_string( xform_list, ';' );
970 for (i = 0; xforms && xforms[i] && r == ERROR_SUCCESS; i++)
972 if (xforms[i][0] == ':')
973 r = apply_substorage_transform( package, package->db, xforms[i] );
974 else
976 WCHAR *transform;
978 if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
979 else
981 WCHAR *p = strrchrW( package->PackagePath, '\\' );
982 DWORD len = p - package->PackagePath + 1;
984 if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
986 msi_free( xforms );
987 msi_free( xform_list );
988 return ERROR_OUTOFMEMORY;
990 memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
991 memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
993 r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
994 if (transform != xforms[i]) msi_free( transform );
997 msi_free( xforms );
998 msi_free( xform_list );
999 return r;
1002 UINT msi_apply_registered_patch( MSIPACKAGE *package, LPCWSTR patch_code )
1004 UINT r;
1005 DWORD len;
1006 WCHAR patch_file[MAX_PATH];
1007 MSIDATABASE *patch_db;
1008 MSIPATCHINFO *patch_info;
1009 MSISUMMARYINFO *si;
1011 TRACE("%p, %s\n", package, debugstr_w(patch_code));
1013 len = sizeof(patch_file) / sizeof(WCHAR);
1014 r = MsiGetPatchInfoExW( patch_code, package->ProductCode, NULL, package->Context,
1015 INSTALLPROPERTY_LOCALPACKAGEW, patch_file, &len );
1016 if (r != ERROR_SUCCESS)
1018 ERR("failed to get patch filename %u\n", r);
1019 return r;
1021 r = MSI_OpenDatabaseW( patch_file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
1022 if (r != ERROR_SUCCESS)
1024 ERR("failed to open patch database %s\n", debugstr_w( patch_file ));
1025 return r;
1027 r = msi_get_suminfo( patch_db->storage, 0, &si );
1028 if (r != ERROR_SUCCESS)
1030 msiobj_release( &patch_db->hdr );
1031 return r;
1033 r = msi_parse_patch_summary( si, &patch_info );
1034 msiobj_release( &si->hdr );
1035 if (r != ERROR_SUCCESS)
1037 ERR("failed to parse patch summary %u\n", r);
1038 msiobj_release( &patch_db->hdr );
1039 return r;
1041 patch_info->registered = TRUE;
1042 patch_info->localfile = strdupW( patch_file );
1043 if (!patch_info->localfile)
1045 msiobj_release( &patch_db->hdr );
1046 msi_free_patchinfo( patch_info );
1047 return ERROR_OUTOFMEMORY;
1049 r = msi_apply_patch_db( package, patch_db, patch_info );
1050 msiobj_release( &patch_db->hdr );
1051 if (r != ERROR_SUCCESS)
1053 ERR("failed to apply patch %u\n", r);
1054 msi_free_patchinfo( patch_info );
1056 return r;