gdi32: Move the font cache list out of freetype.c.
[wine.git] / dlls / msi / patch.c
blob67da57784158ffa172cc7e009e187fdf16962d64
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 "msipriv.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(msi);
34 static BOOL match_language( MSIPACKAGE *package, LANGID langid )
36 UINT i;
38 if (!package->num_langids || !langid) return TRUE;
39 for (i = 0; i < package->num_langids; i++)
41 if (package->langids[i] == langid) return TRUE;
43 return FALSE;
46 struct transform_desc
48 WCHAR *product_code_from;
49 WCHAR *product_code_to;
50 WCHAR *version_from;
51 WCHAR *version_to;
52 WCHAR *upgrade_code;
55 static void free_transform_desc( struct transform_desc *desc )
57 msi_free( desc->product_code_from );
58 msi_free( desc->product_code_to );
59 msi_free( desc->version_from );
60 msi_free( desc->version_to );
61 msi_free( desc->upgrade_code );
62 msi_free( desc );
65 static struct transform_desc *parse_transform_desc( const WCHAR *str )
67 struct transform_desc *ret;
68 const WCHAR *p = str, *q;
69 UINT len;
71 if (!(ret = msi_alloc_zero( sizeof(*ret) ))) return NULL;
73 q = wcschr( p, '}' );
74 if (*p != '{' || !q) goto error;
76 len = q - p + 1;
77 if (!(ret->product_code_from = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error;
78 memcpy( ret->product_code_from, p, len * sizeof(WCHAR) );
79 ret->product_code_from[len] = 0;
81 p = q + 1;
82 if (!(q = wcschr( p, ';' ))) goto error;
83 len = q - p;
84 if (!(ret->version_from = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error;
85 memcpy( ret->version_from, p, len * sizeof(WCHAR) );
86 ret->version_from[len] = 0;
88 p = q + 1;
89 q = wcschr( p, '}' );
90 if (*p != '{' || !q) goto error;
92 len = q - p + 1;
93 if (!(ret->product_code_to = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error;
94 memcpy( ret->product_code_to, p, len * sizeof(WCHAR) );
95 ret->product_code_to[len] = 0;
97 p = q + 1;
98 if (!(q = wcschr( p, ';' ))) goto error;
99 len = q - p;
100 if (!(ret->version_to = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error;
101 memcpy( ret->version_to, p, len * sizeof(WCHAR) );
102 ret->version_to[len] = 0;
104 p = q + 1;
105 q = wcschr( p, '}' );
106 if (*p != '{' || !q) goto error;
108 len = q - p + 1;
109 if (!(ret->upgrade_code = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error;
110 memcpy( ret->upgrade_code, p, len * sizeof(WCHAR) );
111 ret->upgrade_code[len] = 0;
113 return ret;
115 error:
116 free_transform_desc( ret );
117 return NULL;
120 static UINT check_transform_applicable( MSIPACKAGE *package, IStorage *transform )
122 static const UINT supported_flags =
123 MSITRANSFORM_VALIDATE_PRODUCT | MSITRANSFORM_VALIDATE_LANGUAGE |
124 MSITRANSFORM_VALIDATE_PLATFORM | MSITRANSFORM_VALIDATE_MAJORVERSION |
125 MSITRANSFORM_VALIDATE_MINORVERSION | MSITRANSFORM_VALIDATE_UPGRADECODE;
126 MSISUMMARYINFO *si;
127 UINT r, valid_flags = 0, wanted_flags = 0;
128 WCHAR *template, *product, *p;
129 struct transform_desc *desc;
131 r = msi_get_suminfo( transform, 0, &si );
132 if (r != ERROR_SUCCESS)
134 WARN("no summary information!\n");
135 return r;
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 /* native is not validating platform */
142 wanted_flags &= ~MSITRANSFORM_VALIDATE_PLATFORM;
144 if (wanted_flags & ~supported_flags)
146 FIXME("unsupported validation flags 0x%04x\n", wanted_flags);
147 msiobj_release( &si->hdr );
148 return ERROR_FUNCTION_FAILED;
150 if (!(template = msi_suminfo_dup_string( si, PID_TEMPLATE )))
152 WARN("no template property!\n");
153 msiobj_release( &si->hdr );
154 return ERROR_FUNCTION_FAILED;
156 TRACE("template property: %s\n", debugstr_w(template));
157 if (!(product = msi_get_suminfo_product( transform )))
159 WARN("no product property!\n");
160 msi_free( template );
161 msiobj_release( &si->hdr );
162 return ERROR_FUNCTION_FAILED;
164 TRACE("product property: %s\n", debugstr_w(product));
165 if (!(desc = parse_transform_desc( product )))
167 msi_free( template );
168 msiobj_release( &si->hdr );
169 return ERROR_FUNCTION_FAILED;
171 msi_free( product );
173 if (wanted_flags & MSITRANSFORM_VALIDATE_LANGUAGE)
175 if (!template[0] || ((p = wcschr( template, ';' )) && match_language( package, wcstol( p + 1, NULL, 10 ) )))
177 valid_flags |= MSITRANSFORM_VALIDATE_LANGUAGE;
180 if (wanted_flags & MSITRANSFORM_VALIDATE_PRODUCT)
182 WCHAR *product_code_installed = msi_dup_property( package->db, szProductCode );
184 if (!product_code_installed)
186 msi_free( template );
187 free_transform_desc( desc );
188 msiobj_release( &si->hdr );
189 return ERROR_INSTALL_PACKAGE_INVALID;
191 if (!wcscmp( desc->product_code_from, product_code_installed ))
193 valid_flags |= MSITRANSFORM_VALIDATE_PRODUCT;
195 msi_free( product_code_installed );
197 msi_free( template );
198 if (wanted_flags & MSITRANSFORM_VALIDATE_MAJORVERSION)
200 WCHAR *product_version_installed = msi_dup_property( package->db, szProductVersion );
201 DWORD major_installed, minor_installed, major, minor;
203 if (!product_version_installed)
205 free_transform_desc( desc );
206 msiobj_release( &si->hdr );
207 return ERROR_INSTALL_PACKAGE_INVALID;
209 msi_parse_version_string( product_version_installed, &major_installed, &minor_installed );
210 msi_parse_version_string( desc->version_from, &major, &minor );
212 if (major_installed == major)
214 valid_flags |= MSITRANSFORM_VALIDATE_MAJORVERSION;
215 wanted_flags &= ~MSITRANSFORM_VALIDATE_MINORVERSION;
217 msi_free( product_version_installed );
219 else if (wanted_flags & MSITRANSFORM_VALIDATE_MINORVERSION)
221 WCHAR *product_version_installed = msi_dup_property( package->db, szProductVersion );
222 DWORD major_installed, minor_installed, major, minor;
224 if (!product_version_installed)
226 free_transform_desc( desc );
227 msiobj_release( &si->hdr );
228 return ERROR_INSTALL_PACKAGE_INVALID;
230 msi_parse_version_string( product_version_installed, &major_installed, &minor_installed );
231 msi_parse_version_string( desc->version_from, &major, &minor );
233 if (major_installed == major && minor_installed == minor)
234 valid_flags |= MSITRANSFORM_VALIDATE_MINORVERSION;
235 msi_free( product_version_installed );
237 if (wanted_flags & MSITRANSFORM_VALIDATE_UPGRADECODE)
239 WCHAR *upgrade_code_installed = msi_dup_property( package->db, szUpgradeCode );
241 if (!upgrade_code_installed)
243 free_transform_desc( desc );
244 msiobj_release( &si->hdr );
245 return ERROR_INSTALL_PACKAGE_INVALID;
247 if (!wcscmp( desc->upgrade_code, upgrade_code_installed ))
248 valid_flags |= MSITRANSFORM_VALIDATE_UPGRADECODE;
249 msi_free( upgrade_code_installed );
252 free_transform_desc( desc );
253 msiobj_release( &si->hdr );
254 if ((valid_flags & wanted_flags) != wanted_flags) return ERROR_FUNCTION_FAILED;
255 TRACE("applicable transform\n");
256 return ERROR_SUCCESS;
259 static UINT apply_substorage_transform( MSIPACKAGE *package, MSIDATABASE *patch_db, LPCWSTR name )
261 UINT ret = ERROR_FUNCTION_FAILED;
262 IStorage *stg = NULL;
263 HRESULT r;
265 TRACE("%p %s\n", package, debugstr_w(name));
267 if (*name++ != ':')
269 ERR("expected a colon in %s\n", debugstr_w(name));
270 return ERROR_FUNCTION_FAILED;
272 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
273 if (SUCCEEDED(r))
275 ret = check_transform_applicable( package, stg );
276 if (ret == ERROR_SUCCESS)
278 msi_table_apply_transform( package->db, stg, MSITRANSFORM_ERROR_VIEWTRANSFORM );
279 msi_table_apply_transform( package->db, stg, 0 );
281 else
283 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
285 IStorage_Release( stg );
287 else
289 ERR("failed to open substorage %s\n", debugstr_w(name));
291 return ERROR_SUCCESS;
294 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
296 LPWSTR guid_list, *guids, product_code;
297 UINT i, ret = ERROR_FUNCTION_FAILED;
299 product_code = msi_dup_property( package->db, szProductCode );
300 if (!product_code)
302 /* FIXME: the property ProductCode should be written into the DB somewhere */
303 ERR("no product code to check\n");
304 return ERROR_SUCCESS;
306 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
307 guids = msi_split_string( guid_list, ';' );
308 for (i = 0; guids[i] && ret != ERROR_SUCCESS; i++)
310 if (!wcscmp( guids[i], product_code )) ret = ERROR_SUCCESS;
312 msi_free( guids );
313 msi_free( guid_list );
314 msi_free( product_code );
315 return ret;
318 static UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
320 MSIPATCHINFO *pi;
321 UINT r = ERROR_SUCCESS;
322 WCHAR *p;
324 if (!(pi = msi_alloc_zero( sizeof(MSIPATCHINFO) )))
326 return ERROR_OUTOFMEMORY;
328 if (!(pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER )))
330 msi_free( pi );
331 return ERROR_OUTOFMEMORY;
333 p = pi->patchcode;
334 if (*p != '{')
336 msi_free( pi->patchcode );
337 msi_free( pi );
338 return ERROR_PATCH_PACKAGE_INVALID;
340 if (!(p = wcschr( p + 1, '}' )))
342 msi_free( pi->patchcode );
343 msi_free( pi );
344 return ERROR_PATCH_PACKAGE_INVALID;
346 if (p[1])
348 FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
349 p[1] = 0;
351 TRACE("patch code %s\n", debugstr_w(pi->patchcode));
352 if (!(pi->products = msi_suminfo_dup_string( si, PID_TEMPLATE )))
354 msi_free( pi->patchcode );
355 msi_free( pi );
356 return ERROR_OUTOFMEMORY;
358 if (!(pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR )))
360 msi_free( pi->patchcode );
361 msi_free( pi->products );
362 msi_free( pi );
363 return ERROR_OUTOFMEMORY;
365 *patch = pi;
366 return r;
369 static UINT patch_set_media_source_prop( MSIPACKAGE *package )
371 static const WCHAR query[] = {
372 'S','E','L','E','C','T',' ','`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
373 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ',
374 'I','S',' ','N','O','T',' ','N','U','L','L',0};
375 MSIQUERY *view;
376 MSIRECORD *rec;
377 const WCHAR *property;
378 WCHAR *patch;
379 UINT r;
381 r = MSI_DatabaseOpenViewW( package->db, query, &view );
382 if (r != ERROR_SUCCESS)
383 return r;
385 r = MSI_ViewExecute( view, 0 );
386 if (r != ERROR_SUCCESS)
387 goto done;
389 if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
391 property = MSI_RecordGetString( rec, 1 );
392 patch = msi_dup_property( package->db, szPatch );
393 msi_set_property( package->db, property, patch, -1 );
394 msi_free( patch );
395 msiobj_release( &rec->hdr );
398 done:
399 msiobj_release( &view->hdr );
400 return r;
403 struct patch_offset
405 struct list entry;
406 WCHAR *name;
407 UINT sequence;
410 struct patch_offset_list
412 struct list files;
413 struct list patches;
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 list_init( &pos->patches );
423 pos->count = pos->max = 0;
424 pos->min = 999999;
425 return pos;
428 static void patch_offset_list_free( struct patch_offset_list *pos )
430 struct patch_offset *po, *po2;
432 LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct patch_offset, entry )
434 msi_free( po->name );
435 msi_free( po );
437 LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->patches, struct patch_offset, entry )
439 msi_free( po->name );
440 msi_free( po );
442 msi_free( pos );
445 static void patch_offset_get_filepatches( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
447 static const WCHAR query[] = {
448 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
449 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
450 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
451 MSIQUERY *view;
452 MSIRECORD *rec;
453 UINT r;
455 r = MSI_DatabaseOpenViewW( db, query, &view );
456 if (r != ERROR_SUCCESS)
457 return;
459 rec = MSI_CreateRecord( 1 );
460 MSI_RecordSetInteger( rec, 1, last_sequence );
462 r = MSI_ViewExecute( view, rec );
463 msiobj_release( &rec->hdr );
464 if (r != ERROR_SUCCESS)
465 return;
467 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
469 struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) );
471 po->name = msi_dup_record_field( rec, 1 );
472 po->sequence = MSI_RecordGetInteger( rec, 2 );
473 pos->min = min( pos->min, po->sequence );
474 pos->max = max( pos->max, po->sequence );
475 list_add_tail( &pos->patches, &po->entry );
476 pos->count++;
478 msiobj_release( &rec->hdr );
480 msiobj_release( &view->hdr );
483 static void patch_offset_get_files( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
485 static const WCHAR query[] = {
486 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
487 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
488 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
489 MSIQUERY *view;
490 MSIRECORD *rec;
491 UINT r;
493 r = MSI_DatabaseOpenViewW( db, query, &view );
494 if (r != ERROR_SUCCESS)
495 return;
497 rec = MSI_CreateRecord( 1 );
498 MSI_RecordSetInteger( rec, 1, last_sequence );
500 r = MSI_ViewExecute( view, rec );
501 msiobj_release( &rec->hdr );
502 if (r != ERROR_SUCCESS)
503 return;
505 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
507 UINT attributes = MSI_RecordGetInteger( rec, 7 );
508 if (attributes & msidbFileAttributesPatchAdded)
510 struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) );
512 po->name = msi_dup_record_field( rec, 1 );
513 po->sequence = MSI_RecordGetInteger( rec, 8 );
514 pos->min = min( pos->min, po->sequence );
515 pos->max = max( pos->max, po->sequence );
516 list_add_tail( &pos->files, &po->entry );
517 pos->count++;
519 msiobj_release( &rec->hdr );
521 msiobj_release( &view->hdr );
524 static UINT patch_update_file_sequence( MSIDATABASE *db, const struct patch_offset_list *pos,
525 MSIQUERY *view, MSIRECORD *rec )
527 struct patch_offset *po;
528 const WCHAR *file = MSI_RecordGetString( rec, 1 );
529 UINT r = ERROR_SUCCESS, seq = MSI_RecordGetInteger( rec, 8 );
531 LIST_FOR_EACH_ENTRY( po, &pos->files, struct patch_offset, entry )
533 if (!wcsicmp( file, po->name ))
535 MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply );
536 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
537 if (r != ERROR_SUCCESS)
538 ERR("Failed to update offset for file %s (%u)\n", debugstr_w(file), r);
539 break;
542 return r;
545 static UINT patch_update_filepatch_sequence( MSIDATABASE *db, const struct patch_offset_list *pos,
546 MSIQUERY *view, MSIRECORD *rec )
548 static const WCHAR delete_query[] = {
549 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','P','a','t','c','h','`',' ',
550 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','?',' ',
551 'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','=',' ','?',0};
552 static const WCHAR insert_query[] = {
553 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','P','a','t','c','h','`',' ',
554 '(','`','F','i','l','e','_','`',',','`','S','e','q','u','e','n','c','e','`',',',
555 '`','P','a','t','c','h','S','i','z','e','`',',','`','A','t','t','r','i','b','u','t','e','s','`',',',
556 '`','H','e','a','d','e','r','`',',','`','S','t','r','e','a','m','R','e','f','_','`',')',' ',
557 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
558 struct patch_offset *po;
559 const WCHAR *file = MSI_RecordGetString( rec, 1 );
560 UINT r = ERROR_SUCCESS, seq = MSI_RecordGetInteger( rec, 2 );
562 LIST_FOR_EACH_ENTRY( po, &pos->patches, struct patch_offset, entry )
564 if (seq == po->sequence && !wcsicmp( file, po->name ))
566 MSIQUERY *delete_view, *insert_view;
567 MSIRECORD *rec2;
569 r = MSI_DatabaseOpenViewW( db, delete_query, &delete_view );
570 if (r != ERROR_SUCCESS) return r;
572 rec2 = MSI_CreateRecord( 2 );
573 MSI_RecordSetStringW( rec2, 1, po->name );
574 MSI_RecordSetInteger( rec2, 2, po->sequence );
575 r = MSI_ViewExecute( delete_view, rec2 );
576 msiobj_release( &delete_view->hdr );
577 msiobj_release( &rec2->hdr );
578 if (r != ERROR_SUCCESS) return r;
580 r = MSI_DatabaseOpenViewW( db, insert_query, &insert_view );
581 if (r != ERROR_SUCCESS) return r;
583 MSI_RecordSetInteger( rec, 2, po->sequence + pos->offset_to_apply );
585 r = MSI_ViewExecute( insert_view, rec );
586 msiobj_release( &insert_view->hdr );
587 if (r != ERROR_SUCCESS)
588 ERR("Failed to update offset for filepatch %s (%u)\n", debugstr_w(file), r);
589 break;
592 return r;
595 static UINT patch_offset_modify_db( MSIDATABASE *db, struct patch_offset_list *pos )
597 static const WCHAR file_query[] = {
598 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','i','l','e','`',' ',
599 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>','=',' ','?',' ',
600 'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','<','=',' ','?',' ',
601 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
602 static const WCHAR patch_query[] = {
603 'S','E','L','E','C','T',' ','*','F','R','O','M',' ','`','P','a','t','c','h','`',' ',
604 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>','=',' ','?',' ',
605 'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','<','=',' ','?',' ',
606 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
607 MSIRECORD *rec;
608 MSIQUERY *view;
609 UINT r, min = pos->min, max = pos->max, r_fetch;
611 r = MSI_DatabaseOpenViewW( db, file_query, &view );
612 if (r != ERROR_SUCCESS)
613 return ERROR_SUCCESS;
615 rec = MSI_CreateRecord( 2 );
616 MSI_RecordSetInteger( rec, 1, min );
617 MSI_RecordSetInteger( rec, 2, max );
619 r = MSI_ViewExecute( view, rec );
620 msiobj_release( &rec->hdr );
621 if (r != ERROR_SUCCESS)
622 goto done;
624 while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
626 r = patch_update_file_sequence( db, pos, view, rec );
627 msiobj_release( &rec->hdr );
628 if (r != ERROR_SUCCESS) goto done;
630 msiobj_release( &view->hdr );
632 r = MSI_DatabaseOpenViewW( db, patch_query, &view );
633 if (r != ERROR_SUCCESS)
634 return ERROR_SUCCESS;
636 rec = MSI_CreateRecord( 2 );
637 MSI_RecordSetInteger( rec, 1, min );
638 MSI_RecordSetInteger( rec, 2, max );
640 r = MSI_ViewExecute( view, rec );
641 msiobj_release( &rec->hdr );
642 if (r != ERROR_SUCCESS)
643 goto done;
645 while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
647 r = patch_update_filepatch_sequence( db, pos, view, rec );
648 msiobj_release( &rec->hdr );
649 if (r != ERROR_SUCCESS) goto done;
652 done:
653 msiobj_release( &view->hdr );
654 return r;
657 static const WCHAR patch_media_query[] = {
658 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
659 'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
660 'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
661 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
663 struct patch_media
665 struct list entry;
666 UINT disk_id;
667 UINT last_sequence;
668 WCHAR *prompt;
669 WCHAR *cabinet;
670 WCHAR *volume;
671 WCHAR *source;
674 static UINT patch_add_media( MSIPACKAGE *package, IStorage *storage, MSIPATCHINFO *patch )
676 static const WCHAR delete_query[] = {
677 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
678 'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0};
679 static const WCHAR insert_query[] = {
680 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ',
681 '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',',
682 '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',',
683 '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ',
684 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
685 MSIQUERY *view;
686 MSIRECORD *rec;
687 UINT r, disk_id;
688 struct list media_list;
689 struct patch_media *media, *next;
691 r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view );
692 if (r != ERROR_SUCCESS) return r;
694 r = MSI_ViewExecute( view, 0 );
695 if (r != ERROR_SUCCESS)
697 msiobj_release( &view->hdr );
698 TRACE("query failed %u\n", r);
699 return r;
701 list_init( &media_list );
702 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
704 disk_id = MSI_RecordGetInteger( rec, 1 );
705 TRACE("disk_id %u\n", disk_id);
706 if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
708 msiobj_release( &rec->hdr );
709 continue;
711 if (!(media = msi_alloc( sizeof( *media )))) {
712 msiobj_release( &rec->hdr );
713 goto done;
715 media->disk_id = disk_id;
716 media->last_sequence = MSI_RecordGetInteger( rec, 2 );
717 media->prompt = msi_dup_record_field( rec, 3 );
718 media->cabinet = msi_dup_record_field( rec, 4 );
719 media->volume = msi_dup_record_field( rec, 5 );
720 media->source = msi_dup_record_field( rec, 6 );
722 list_add_tail( &media_list, &media->entry );
723 msiobj_release( &rec->hdr );
725 LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry )
727 MSIQUERY *delete_view, *insert_view;
729 r = MSI_DatabaseOpenViewW( package->db, delete_query, &delete_view );
730 if (r != ERROR_SUCCESS) goto done;
732 rec = MSI_CreateRecord( 1 );
733 MSI_RecordSetInteger( rec, 1, media->disk_id );
735 r = MSI_ViewExecute( delete_view, rec );
736 msiobj_release( &delete_view->hdr );
737 msiobj_release( &rec->hdr );
738 if (r != ERROR_SUCCESS) goto done;
740 r = MSI_DatabaseOpenViewW( package->db, insert_query, &insert_view );
741 if (r != ERROR_SUCCESS) goto done;
743 disk_id = package->db->media_transform_disk_id;
744 TRACE("disk id %u\n", disk_id);
745 TRACE("last sequence %u\n", media->last_sequence);
746 TRACE("prompt %s\n", debugstr_w(media->prompt));
747 TRACE("cabinet %s\n", debugstr_w(media->cabinet));
748 TRACE("volume %s\n", debugstr_w(media->volume));
749 TRACE("source %s\n", debugstr_w(media->source));
751 rec = MSI_CreateRecord( 6 );
752 MSI_RecordSetInteger( rec, 1, disk_id );
753 MSI_RecordSetInteger( rec, 2, media->last_sequence );
754 MSI_RecordSetStringW( rec, 3, media->prompt );
755 MSI_RecordSetStringW( rec, 4, media->cabinet );
756 MSI_RecordSetStringW( rec, 5, media->volume );
757 MSI_RecordSetStringW( rec, 6, media->source );
759 r = MSI_ViewExecute( insert_view, rec );
760 msiobj_release( &insert_view->hdr );
761 msiobj_release( &rec->hdr );
762 if (r != ERROR_SUCCESS) goto done;
764 r = msi_add_cabinet_stream( package, disk_id, storage, media->cabinet );
765 if (r != ERROR_SUCCESS) ERR("failed to add cabinet stream %u\n", r);
766 else
768 patch->disk_id = disk_id;
769 package->db->media_transform_disk_id++;
773 done:
774 msiobj_release( &view->hdr );
775 LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry )
777 list_remove( &media->entry );
778 msi_free( media->prompt );
779 msi_free( media->cabinet );
780 msi_free( media->volume );
781 msi_free( media->source );
782 msi_free( media );
784 return r;
787 static UINT patch_set_offsets( MSIDATABASE *db, MSIPATCHINFO *patch )
789 MSIQUERY *view;
790 MSIRECORD *rec;
791 UINT r;
793 r = MSI_DatabaseOpenViewW( db, patch_media_query, &view );
794 if (r != ERROR_SUCCESS)
795 return r;
797 r = MSI_ViewExecute( view, 0 );
798 if (r != ERROR_SUCCESS)
799 goto done;
801 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
803 UINT offset, last_sequence = MSI_RecordGetInteger( rec, 2 );
804 struct patch_offset_list *pos;
806 /* FIXME: set/check Source field instead? */
807 if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
809 msiobj_release( &rec->hdr );
810 continue;
812 pos = patch_offset_list_create();
813 patch_offset_get_files( db, last_sequence, pos );
814 patch_offset_get_filepatches( db, last_sequence, pos );
816 offset = db->media_transform_offset - pos->min;
817 last_sequence = offset + pos->max;
819 last_sequence += pos->min;
820 pos->offset_to_apply = offset;
821 if (pos->count)
823 r = patch_offset_modify_db( db, pos );
824 if (r != ERROR_SUCCESS)
825 ERR("Failed to set offsets, expect breakage (%u)\n", r);
827 MSI_RecordSetInteger( rec, 2, last_sequence );
828 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
829 if (r != ERROR_SUCCESS)
830 ERR("Failed to update Media table entry, expect breakage (%u)\n", r);
832 db->media_transform_offset = last_sequence + 1;
834 patch_offset_list_free( pos );
835 msiobj_release( &rec->hdr );
838 done:
839 msiobj_release( &view->hdr );
840 return r;
843 static DWORD is_uninstallable( MSIDATABASE *db )
845 static const WCHAR query[] = {
846 'S','E','L','E','C','T',' ','`','V','a','l','u','e','`',' ','F','R','O','M',' ',
847 '`','M','s','i','P','a','t','c','h','M','e','t','a','d','a','t','a','`',' ',
848 'W','H','E','R','E',' ','`','C','o','m','p','a','n','y','`',' ','I','S',' ',
849 'N','U','L','L',' ','A','N','D',' ','`','P','r','o','p','e','r','t','y','`','=',
850 '\'','A','l','l','o','w','R','e','m','o','v','a','l','\'',0};
851 MSIQUERY *view;
852 MSIRECORD *rec;
853 DWORD ret = 0;
855 if (MSI_DatabaseOpenViewW( db, query, &view ) != ERROR_SUCCESS) return 0;
856 if (MSI_ViewExecute( view, 0 ) != ERROR_SUCCESS)
858 msiobj_release( &view->hdr );
859 return 0;
862 if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
864 const WCHAR *value = MSI_RecordGetString( rec, 1 );
865 ret = wcstol( value, NULL, 10 );
866 msiobj_release( &rec->hdr );
869 FIXME( "check other criteria\n" );
871 msiobj_release( &view->hdr );
872 return ret;
875 static UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
877 UINT i, r = ERROR_SUCCESS;
878 WCHAR **substorage;
880 /* apply substorage transforms */
881 substorage = msi_split_string( patch->transforms, ';' );
882 for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
884 r = apply_substorage_transform( package, patch_db, substorage[i] );
885 if (r == ERROR_SUCCESS)
887 r = patch_set_offsets( package->db, patch );
888 if (r == ERROR_SUCCESS)
889 r = patch_add_media( package, patch_db->storage, patch );
892 msi_free( substorage );
893 if (r != ERROR_SUCCESS)
894 return r;
896 r = patch_set_media_source_prop( package );
897 if (r != ERROR_SUCCESS)
898 return r;
900 patch->uninstallable = is_uninstallable( patch_db );
901 patch->state = MSIPATCHSTATE_APPLIED;
902 list_add_tail( &package->patches, &patch->entry );
903 return ERROR_SUCCESS;
906 void msi_free_patchinfo( MSIPATCHINFO *patch )
908 msi_free( patch->patchcode );
909 msi_free( patch->products );
910 msi_free( patch->transforms );
911 msi_free( patch->filename );
912 msi_free( patch->localfile );
913 msi_free( patch );
916 static UINT msi_apply_patch_package( MSIPACKAGE *package, const WCHAR *file )
918 static const WCHAR dotmsp[] = {'.','m','s','p',0};
919 MSIDATABASE *patch_db = NULL;
920 WCHAR localfile[MAX_PATH];
921 MSISUMMARYINFO *si;
922 MSIPATCHINFO *patch = NULL;
923 UINT r;
925 TRACE("%p, %s\n", package, debugstr_w(file));
927 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
928 if (r != ERROR_SUCCESS)
930 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
931 return r;
933 r = msi_get_suminfo( patch_db->storage, 0, &si );
934 if (r != ERROR_SUCCESS)
936 msiobj_release( &patch_db->hdr );
937 return r;
939 r = msi_check_patch_applicable( package, si );
940 if (r != ERROR_SUCCESS)
942 TRACE("patch not applicable\n");
943 r = ERROR_SUCCESS;
944 goto done;
946 r = msi_parse_patch_summary( si, &patch );
947 if ( r != ERROR_SUCCESS )
948 goto done;
950 r = msi_create_empty_local_file( localfile, dotmsp );
951 if ( r != ERROR_SUCCESS )
952 goto done;
954 r = ERROR_OUTOFMEMORY;
955 patch->registered = FALSE;
956 if (!(patch->filename = strdupW( file ))) goto done;
957 if (!(patch->localfile = strdupW( localfile ))) goto done;
959 r = msi_apply_patch_db( package, patch_db, patch );
960 if (r != ERROR_SUCCESS) WARN("patch failed to apply %u\n", r);
962 done:
963 msiobj_release( &si->hdr );
964 msiobj_release( &patch_db->hdr );
965 if (patch && r != ERROR_SUCCESS)
967 DeleteFileW( patch->localfile );
968 msi_free_patchinfo( patch );
970 return r;
973 /* get the PATCH property, and apply all the patches it specifies */
974 UINT msi_apply_patches( MSIPACKAGE *package )
976 LPWSTR patch_list, *patches;
977 UINT i, r = ERROR_SUCCESS;
979 patch_list = msi_dup_property( package->db, szPatch );
981 TRACE("patches to be applied: %s\n", debugstr_w(patch_list));
983 patches = msi_split_string( patch_list, ';' );
984 for (i = 0; patches && patches[i] && r == ERROR_SUCCESS; i++)
985 r = msi_apply_patch_package( package, patches[i] );
987 msi_free( patches );
988 msi_free( patch_list );
989 return r;
992 UINT msi_apply_transforms( MSIPACKAGE *package )
994 static const WCHAR szTransforms[] = {'T','R','A','N','S','F','O','R','M','S',0};
995 LPWSTR xform_list, *xforms;
996 UINT i, r = ERROR_SUCCESS;
998 xform_list = msi_dup_property( package->db, szTransforms );
999 xforms = msi_split_string( xform_list, ';' );
1001 for (i = 0; xforms && xforms[i] && r == ERROR_SUCCESS; i++)
1003 if (xforms[i][0] == ':')
1004 r = apply_substorage_transform( package, package->db, xforms[i] );
1005 else
1007 WCHAR *transform;
1009 if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
1010 else
1012 WCHAR *p = wcsrchr( package->PackagePath, '\\' );
1013 DWORD len = p - package->PackagePath + 1;
1015 if (!(transform = msi_alloc( (len + lstrlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
1017 msi_free( xforms );
1018 msi_free( xform_list );
1019 return ERROR_OUTOFMEMORY;
1021 memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
1022 memcpy( transform + len, xforms[i], (lstrlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
1024 r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
1025 if (transform != xforms[i]) msi_free( transform );
1028 msi_free( xforms );
1029 msi_free( xform_list );
1030 return r;
1033 UINT msi_apply_registered_patch( MSIPACKAGE *package, LPCWSTR patch_code )
1035 UINT r;
1036 DWORD len;
1037 WCHAR patch_file[MAX_PATH];
1038 MSIDATABASE *patch_db;
1039 MSIPATCHINFO *patch_info;
1040 MSISUMMARYINFO *si;
1042 TRACE("%p, %s\n", package, debugstr_w(patch_code));
1044 len = ARRAY_SIZE( patch_file );
1045 r = MsiGetPatchInfoExW( patch_code, package->ProductCode, NULL, package->Context,
1046 INSTALLPROPERTY_LOCALPACKAGEW, patch_file, &len );
1047 if (r != ERROR_SUCCESS)
1049 ERR("failed to get patch filename %u\n", r);
1050 return r;
1052 r = MSI_OpenDatabaseW( patch_file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
1053 if (r != ERROR_SUCCESS)
1055 ERR("failed to open patch database %s\n", debugstr_w( patch_file ));
1056 return r;
1058 r = msi_get_suminfo( patch_db->storage, 0, &si );
1059 if (r != ERROR_SUCCESS)
1061 msiobj_release( &patch_db->hdr );
1062 return r;
1064 r = msi_parse_patch_summary( si, &patch_info );
1065 msiobj_release( &si->hdr );
1066 if (r != ERROR_SUCCESS)
1068 ERR("failed to parse patch summary %u\n", r);
1069 msiobj_release( &patch_db->hdr );
1070 return r;
1072 patch_info->registered = TRUE;
1073 patch_info->localfile = strdupW( patch_file );
1074 if (!patch_info->localfile)
1076 msiobj_release( &patch_db->hdr );
1077 msi_free_patchinfo( patch_info );
1078 return ERROR_OUTOFMEMORY;
1080 r = msi_apply_patch_db( package, patch_db, patch_info );
1081 msiobj_release( &patch_db->hdr );
1082 if (r != ERROR_SUCCESS)
1084 ERR("failed to apply patch %u\n", r);
1085 msi_free_patchinfo( patch_info );
1087 return r;