ntdll: Load .so builtin modules without using libwine.
[wine/zf.git] / dlls / msi / patch.c
blob35b8463858e31d181936ff15cb714c916105108f
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)
277 msi_table_apply_transform( package->db, stg );
278 else
279 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
280 IStorage_Release( stg );
282 else
284 ERR("failed to open substorage %s\n", debugstr_w(name));
286 return ERROR_SUCCESS;
289 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
291 LPWSTR guid_list, *guids, product_code;
292 UINT i, ret = ERROR_FUNCTION_FAILED;
294 product_code = msi_dup_property( package->db, szProductCode );
295 if (!product_code)
297 /* FIXME: the property ProductCode should be written into the DB somewhere */
298 ERR("no product code to check\n");
299 return ERROR_SUCCESS;
301 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
302 guids = msi_split_string( guid_list, ';' );
303 for (i = 0; guids[i] && ret != ERROR_SUCCESS; i++)
305 if (!wcscmp( guids[i], product_code )) ret = ERROR_SUCCESS;
307 msi_free( guids );
308 msi_free( guid_list );
309 msi_free( product_code );
310 return ret;
313 static UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
315 MSIPATCHINFO *pi;
316 UINT r = ERROR_SUCCESS;
317 WCHAR *p;
319 if (!(pi = msi_alloc_zero( sizeof(MSIPATCHINFO) )))
321 return ERROR_OUTOFMEMORY;
323 if (!(pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER )))
325 msi_free( pi );
326 return ERROR_OUTOFMEMORY;
328 p = pi->patchcode;
329 if (*p != '{')
331 msi_free( pi->patchcode );
332 msi_free( pi );
333 return ERROR_PATCH_PACKAGE_INVALID;
335 if (!(p = wcschr( p + 1, '}' )))
337 msi_free( pi->patchcode );
338 msi_free( pi );
339 return ERROR_PATCH_PACKAGE_INVALID;
341 if (p[1])
343 FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
344 p[1] = 0;
346 TRACE("patch code %s\n", debugstr_w(pi->patchcode));
347 if (!(pi->products = msi_suminfo_dup_string( si, PID_TEMPLATE )))
349 msi_free( pi->patchcode );
350 msi_free( pi );
351 return ERROR_OUTOFMEMORY;
353 if (!(pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR )))
355 msi_free( pi->patchcode );
356 msi_free( pi->products );
357 msi_free( pi );
358 return ERROR_OUTOFMEMORY;
360 *patch = pi;
361 return r;
364 static UINT patch_set_media_source_prop( MSIPACKAGE *package )
366 static const WCHAR query[] = {
367 'S','E','L','E','C','T',' ','`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
368 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ',
369 'I','S',' ','N','O','T',' ','N','U','L','L',0};
370 MSIQUERY *view;
371 MSIRECORD *rec;
372 const WCHAR *property;
373 WCHAR *patch;
374 UINT r;
376 r = MSI_DatabaseOpenViewW( package->db, query, &view );
377 if (r != ERROR_SUCCESS)
378 return r;
380 r = MSI_ViewExecute( view, 0 );
381 if (r != ERROR_SUCCESS)
382 goto done;
384 if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
386 property = MSI_RecordGetString( rec, 1 );
387 patch = msi_dup_property( package->db, szPatch );
388 msi_set_property( package->db, property, patch, -1 );
389 msi_free( patch );
390 msiobj_release( &rec->hdr );
393 done:
394 msiobj_release( &view->hdr );
395 return r;
398 struct patch_offset
400 struct list entry;
401 WCHAR *name;
402 UINT sequence;
405 struct patch_offset_list
407 struct list files;
408 struct list patches;
409 UINT count, min, max;
410 UINT offset_to_apply;
413 static struct patch_offset_list *patch_offset_list_create( void )
415 struct patch_offset_list *pos = msi_alloc( sizeof(struct patch_offset_list) );
416 list_init( &pos->files );
417 list_init( &pos->patches );
418 pos->count = pos->max = 0;
419 pos->min = 999999;
420 return pos;
423 static void patch_offset_list_free( struct patch_offset_list *pos )
425 struct patch_offset *po, *po2;
427 LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct patch_offset, entry )
429 msi_free( po->name );
430 msi_free( po );
432 LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->patches, struct patch_offset, entry )
434 msi_free( po->name );
435 msi_free( po );
437 msi_free( pos );
440 static void patch_offset_get_filepatches( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
442 static const WCHAR query[] = {
443 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
444 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
445 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
446 MSIQUERY *view;
447 MSIRECORD *rec;
448 UINT r;
450 r = MSI_DatabaseOpenViewW( db, query, &view );
451 if (r != ERROR_SUCCESS)
452 return;
454 rec = MSI_CreateRecord( 1 );
455 MSI_RecordSetInteger( rec, 1, last_sequence );
457 r = MSI_ViewExecute( view, rec );
458 msiobj_release( &rec->hdr );
459 if (r != ERROR_SUCCESS)
460 return;
462 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
464 struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) );
466 po->name = msi_dup_record_field( rec, 1 );
467 po->sequence = MSI_RecordGetInteger( rec, 2 );
468 pos->min = min( pos->min, po->sequence );
469 pos->max = max( pos->max, po->sequence );
470 list_add_tail( &pos->patches, &po->entry );
471 pos->count++;
473 msiobj_release( &rec->hdr );
475 msiobj_release( &view->hdr );
478 static void patch_offset_get_files( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
480 static const WCHAR query[] = {
481 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
482 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
483 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
484 MSIQUERY *view;
485 MSIRECORD *rec;
486 UINT r;
488 r = MSI_DatabaseOpenViewW( db, query, &view );
489 if (r != ERROR_SUCCESS)
490 return;
492 rec = MSI_CreateRecord( 1 );
493 MSI_RecordSetInteger( rec, 1, last_sequence );
495 r = MSI_ViewExecute( view, rec );
496 msiobj_release( &rec->hdr );
497 if (r != ERROR_SUCCESS)
498 return;
500 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
502 UINT attributes = MSI_RecordGetInteger( rec, 7 );
503 if (attributes & msidbFileAttributesPatchAdded)
505 struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) );
507 po->name = msi_dup_record_field( rec, 1 );
508 po->sequence = MSI_RecordGetInteger( rec, 8 );
509 pos->min = min( pos->min, po->sequence );
510 pos->max = max( pos->max, po->sequence );
511 list_add_tail( &pos->files, &po->entry );
512 pos->count++;
514 msiobj_release( &rec->hdr );
516 msiobj_release( &view->hdr );
519 static UINT patch_update_file_sequence( MSIDATABASE *db, const struct patch_offset_list *pos,
520 MSIQUERY *view, MSIRECORD *rec )
522 struct patch_offset *po;
523 const WCHAR *file = MSI_RecordGetString( rec, 1 );
524 UINT r = ERROR_SUCCESS, seq = MSI_RecordGetInteger( rec, 8 );
526 LIST_FOR_EACH_ENTRY( po, &pos->files, struct patch_offset, entry )
528 if (!wcsicmp( file, po->name ))
530 MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply );
531 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
532 if (r != ERROR_SUCCESS)
533 ERR("Failed to update offset for file %s (%u)\n", debugstr_w(file), r);
534 break;
537 return r;
540 static UINT patch_update_filepatch_sequence( MSIDATABASE *db, const struct patch_offset_list *pos,
541 MSIQUERY *view, MSIRECORD *rec )
543 static const WCHAR delete_query[] = {
544 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','P','a','t','c','h','`',' ',
545 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','?',' ',
546 'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','=',' ','?',0};
547 static const WCHAR insert_query[] = {
548 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','P','a','t','c','h','`',' ',
549 '(','`','F','i','l','e','_','`',',','`','S','e','q','u','e','n','c','e','`',',',
550 '`','P','a','t','c','h','S','i','z','e','`',',','`','A','t','t','r','i','b','u','t','e','s','`',',',
551 '`','H','e','a','d','e','r','`',',','`','S','t','r','e','a','m','R','e','f','_','`',')',' ',
552 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
553 struct patch_offset *po;
554 const WCHAR *file = MSI_RecordGetString( rec, 1 );
555 UINT r = ERROR_SUCCESS, seq = MSI_RecordGetInteger( rec, 2 );
557 LIST_FOR_EACH_ENTRY( po, &pos->patches, struct patch_offset, entry )
559 if (seq == po->sequence && !wcsicmp( file, po->name ))
561 MSIQUERY *delete_view, *insert_view;
562 MSIRECORD *rec2;
564 r = MSI_DatabaseOpenViewW( db, delete_query, &delete_view );
565 if (r != ERROR_SUCCESS) return r;
567 rec2 = MSI_CreateRecord( 2 );
568 MSI_RecordSetStringW( rec2, 1, po->name );
569 MSI_RecordSetInteger( rec2, 2, po->sequence );
570 r = MSI_ViewExecute( delete_view, rec2 );
571 msiobj_release( &delete_view->hdr );
572 msiobj_release( &rec2->hdr );
573 if (r != ERROR_SUCCESS) return r;
575 r = MSI_DatabaseOpenViewW( db, insert_query, &insert_view );
576 if (r != ERROR_SUCCESS) return r;
578 MSI_RecordSetInteger( rec, 2, po->sequence + pos->offset_to_apply );
580 r = MSI_ViewExecute( insert_view, rec );
581 msiobj_release( &insert_view->hdr );
582 if (r != ERROR_SUCCESS)
583 ERR("Failed to update offset for filepatch %s (%u)\n", debugstr_w(file), r);
584 break;
587 return r;
590 static UINT patch_offset_modify_db( MSIDATABASE *db, struct patch_offset_list *pos )
592 static const WCHAR file_query[] = {
593 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','i','l','e','`',' ',
594 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>','=',' ','?',' ',
595 'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','<','=',' ','?',' ',
596 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
597 static const WCHAR patch_query[] = {
598 'S','E','L','E','C','T',' ','*','F','R','O','M',' ','`','P','a','t','c','h','`',' ',
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 MSIRECORD *rec;
603 MSIQUERY *view;
604 UINT r, min = pos->min, max = pos->max, r_fetch;
606 r = MSI_DatabaseOpenViewW( db, file_query, &view );
607 if (r != ERROR_SUCCESS)
608 return ERROR_SUCCESS;
610 rec = MSI_CreateRecord( 2 );
611 MSI_RecordSetInteger( rec, 1, min );
612 MSI_RecordSetInteger( rec, 2, max );
614 r = MSI_ViewExecute( view, rec );
615 msiobj_release( &rec->hdr );
616 if (r != ERROR_SUCCESS)
617 goto done;
619 while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
621 r = patch_update_file_sequence( db, pos, view, rec );
622 msiobj_release( &rec->hdr );
623 if (r != ERROR_SUCCESS) goto done;
625 msiobj_release( &view->hdr );
627 r = MSI_DatabaseOpenViewW( db, patch_query, &view );
628 if (r != ERROR_SUCCESS)
629 return ERROR_SUCCESS;
631 rec = MSI_CreateRecord( 2 );
632 MSI_RecordSetInteger( rec, 1, min );
633 MSI_RecordSetInteger( rec, 2, max );
635 r = MSI_ViewExecute( view, rec );
636 msiobj_release( &rec->hdr );
637 if (r != ERROR_SUCCESS)
638 goto done;
640 while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
642 r = patch_update_filepatch_sequence( db, pos, view, rec );
643 msiobj_release( &rec->hdr );
644 if (r != ERROR_SUCCESS) goto done;
647 done:
648 msiobj_release( &view->hdr );
649 return r;
652 static const WCHAR patch_media_query[] = {
653 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
654 'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
655 'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
656 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
658 struct patch_media
660 struct list entry;
661 UINT disk_id;
662 UINT last_sequence;
663 WCHAR *prompt;
664 WCHAR *cabinet;
665 WCHAR *volume;
666 WCHAR *source;
669 static UINT patch_add_media( MSIPACKAGE *package, IStorage *storage, MSIPATCHINFO *patch )
671 static const WCHAR delete_query[] = {
672 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
673 'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0};
674 static const WCHAR insert_query[] = {
675 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ',
676 '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',',
677 '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',',
678 '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ',
679 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
680 MSIQUERY *view;
681 MSIRECORD *rec;
682 UINT r, disk_id;
683 struct list media_list;
684 struct patch_media *media, *next;
686 r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view );
687 if (r != ERROR_SUCCESS) return r;
689 r = MSI_ViewExecute( view, 0 );
690 if (r != ERROR_SUCCESS)
692 msiobj_release( &view->hdr );
693 TRACE("query failed %u\n", r);
694 return r;
696 list_init( &media_list );
697 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
699 disk_id = MSI_RecordGetInteger( rec, 1 );
700 TRACE("disk_id %u\n", disk_id);
701 if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
703 msiobj_release( &rec->hdr );
704 continue;
706 if (!(media = msi_alloc( sizeof( *media )))) {
707 msiobj_release( &rec->hdr );
708 goto done;
710 media->disk_id = disk_id;
711 media->last_sequence = MSI_RecordGetInteger( rec, 2 );
712 media->prompt = msi_dup_record_field( rec, 3 );
713 media->cabinet = msi_dup_record_field( rec, 4 );
714 media->volume = msi_dup_record_field( rec, 5 );
715 media->source = msi_dup_record_field( rec, 6 );
717 list_add_tail( &media_list, &media->entry );
718 msiobj_release( &rec->hdr );
720 LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry )
722 MSIQUERY *delete_view, *insert_view;
724 r = MSI_DatabaseOpenViewW( package->db, delete_query, &delete_view );
725 if (r != ERROR_SUCCESS) goto done;
727 rec = MSI_CreateRecord( 1 );
728 MSI_RecordSetInteger( rec, 1, media->disk_id );
730 r = MSI_ViewExecute( delete_view, rec );
731 msiobj_release( &delete_view->hdr );
732 msiobj_release( &rec->hdr );
733 if (r != ERROR_SUCCESS) goto done;
735 r = MSI_DatabaseOpenViewW( package->db, insert_query, &insert_view );
736 if (r != ERROR_SUCCESS) goto done;
738 disk_id = package->db->media_transform_disk_id;
739 TRACE("disk id %u\n", disk_id);
740 TRACE("last sequence %u\n", media->last_sequence);
741 TRACE("prompt %s\n", debugstr_w(media->prompt));
742 TRACE("cabinet %s\n", debugstr_w(media->cabinet));
743 TRACE("volume %s\n", debugstr_w(media->volume));
744 TRACE("source %s\n", debugstr_w(media->source));
746 rec = MSI_CreateRecord( 6 );
747 MSI_RecordSetInteger( rec, 1, disk_id );
748 MSI_RecordSetInteger( rec, 2, media->last_sequence );
749 MSI_RecordSetStringW( rec, 3, media->prompt );
750 MSI_RecordSetStringW( rec, 4, media->cabinet );
751 MSI_RecordSetStringW( rec, 5, media->volume );
752 MSI_RecordSetStringW( rec, 6, media->source );
754 r = MSI_ViewExecute( insert_view, rec );
755 msiobj_release( &insert_view->hdr );
756 msiobj_release( &rec->hdr );
757 if (r != ERROR_SUCCESS) goto done;
759 r = msi_add_cabinet_stream( package, disk_id, storage, media->cabinet );
760 if (r != ERROR_SUCCESS) ERR("failed to add cabinet stream %u\n", r);
761 else
763 patch->disk_id = disk_id;
764 package->db->media_transform_disk_id++;
768 done:
769 msiobj_release( &view->hdr );
770 LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry )
772 list_remove( &media->entry );
773 msi_free( media->prompt );
774 msi_free( media->cabinet );
775 msi_free( media->volume );
776 msi_free( media->source );
777 msi_free( media );
779 return r;
782 static UINT patch_set_offsets( MSIDATABASE *db, MSIPATCHINFO *patch )
784 MSIQUERY *view;
785 MSIRECORD *rec;
786 UINT r;
788 r = MSI_DatabaseOpenViewW( db, patch_media_query, &view );
789 if (r != ERROR_SUCCESS)
790 return r;
792 r = MSI_ViewExecute( view, 0 );
793 if (r != ERROR_SUCCESS)
794 goto done;
796 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
798 UINT offset, last_sequence = MSI_RecordGetInteger( rec, 2 );
799 struct patch_offset_list *pos;
801 /* FIXME: set/check Source field instead? */
802 if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
804 msiobj_release( &rec->hdr );
805 continue;
807 pos = patch_offset_list_create();
808 patch_offset_get_files( db, last_sequence, pos );
809 patch_offset_get_filepatches( db, last_sequence, pos );
811 offset = db->media_transform_offset - pos->min;
812 last_sequence = offset + pos->max;
814 last_sequence += pos->min;
815 pos->offset_to_apply = offset;
816 if (pos->count)
818 r = patch_offset_modify_db( db, pos );
819 if (r != ERROR_SUCCESS)
820 ERR("Failed to set offsets, expect breakage (%u)\n", r);
822 MSI_RecordSetInteger( rec, 2, last_sequence );
823 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
824 if (r != ERROR_SUCCESS)
825 ERR("Failed to update Media table entry, expect breakage (%u)\n", r);
827 db->media_transform_offset = last_sequence + 1;
829 patch_offset_list_free( pos );
830 msiobj_release( &rec->hdr );
833 done:
834 msiobj_release( &view->hdr );
835 return r;
838 static DWORD is_uninstallable( MSIDATABASE *db )
840 static const WCHAR query[] = {
841 'S','E','L','E','C','T',' ','`','V','a','l','u','e','`',' ','F','R','O','M',' ',
842 '`','M','s','i','P','a','t','c','h','M','e','t','a','d','a','t','a','`',' ',
843 'W','H','E','R','E',' ','`','C','o','m','p','a','n','y','`',' ','I','S',' ',
844 'N','U','L','L',' ','A','N','D',' ','`','P','r','o','p','e','r','t','y','`','=',
845 '\'','A','l','l','o','w','R','e','m','o','v','a','l','\'',0};
846 MSIQUERY *view;
847 MSIRECORD *rec;
848 DWORD ret = 0;
850 if (MSI_DatabaseOpenViewW( db, query, &view ) != ERROR_SUCCESS) return 0;
851 if (MSI_ViewExecute( view, 0 ) != ERROR_SUCCESS)
853 msiobj_release( &view->hdr );
854 return 0;
857 if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
859 const WCHAR *value = MSI_RecordGetString( rec, 1 );
860 ret = wcstol( value, NULL, 10 );
861 msiobj_release( &rec->hdr );
864 FIXME( "check other criteria\n" );
866 msiobj_release( &view->hdr );
867 return ret;
870 static UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
872 UINT i, r = ERROR_SUCCESS;
873 WCHAR **substorage;
875 /* apply substorage transforms */
876 substorage = msi_split_string( patch->transforms, ';' );
877 for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
879 r = apply_substorage_transform( package, patch_db, substorage[i] );
880 if (r == ERROR_SUCCESS)
882 r = patch_set_offsets( package->db, patch );
883 if (r == ERROR_SUCCESS)
884 r = patch_add_media( package, patch_db->storage, patch );
887 msi_free( substorage );
888 if (r != ERROR_SUCCESS)
889 return r;
891 r = patch_set_media_source_prop( package );
892 if (r != ERROR_SUCCESS)
893 return r;
895 patch->uninstallable = is_uninstallable( patch_db );
896 patch->state = MSIPATCHSTATE_APPLIED;
897 list_add_tail( &package->patches, &patch->entry );
898 return ERROR_SUCCESS;
901 void msi_free_patchinfo( MSIPATCHINFO *patch )
903 msi_free( patch->patchcode );
904 msi_free( patch->products );
905 msi_free( patch->transforms );
906 msi_free( patch->filename );
907 msi_free( patch->localfile );
908 msi_free( patch );
911 static UINT msi_apply_patch_package( MSIPACKAGE *package, const WCHAR *file )
913 static const WCHAR dotmsp[] = {'.','m','s','p',0};
914 MSIDATABASE *patch_db = NULL;
915 WCHAR localfile[MAX_PATH];
916 MSISUMMARYINFO *si;
917 MSIPATCHINFO *patch = NULL;
918 UINT r;
920 TRACE("%p, %s\n", package, debugstr_w(file));
922 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
923 if (r != ERROR_SUCCESS)
925 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
926 return r;
928 r = msi_get_suminfo( patch_db->storage, 0, &si );
929 if (r != ERROR_SUCCESS)
931 msiobj_release( &patch_db->hdr );
932 return r;
934 r = msi_check_patch_applicable( package, si );
935 if (r != ERROR_SUCCESS)
937 TRACE("patch not applicable\n");
938 r = ERROR_SUCCESS;
939 goto done;
941 r = msi_parse_patch_summary( si, &patch );
942 if ( r != ERROR_SUCCESS )
943 goto done;
945 r = msi_create_empty_local_file( localfile, dotmsp );
946 if ( r != ERROR_SUCCESS )
947 goto done;
949 r = ERROR_OUTOFMEMORY;
950 patch->registered = FALSE;
951 if (!(patch->filename = strdupW( file ))) goto done;
952 if (!(patch->localfile = strdupW( localfile ))) goto done;
954 r = msi_apply_patch_db( package, patch_db, patch );
955 if (r != ERROR_SUCCESS) WARN("patch failed to apply %u\n", r);
957 done:
958 msiobj_release( &si->hdr );
959 msiobj_release( &patch_db->hdr );
960 if (patch && r != ERROR_SUCCESS)
962 DeleteFileW( patch->localfile );
963 msi_free_patchinfo( patch );
965 return r;
968 /* get the PATCH property, and apply all the patches it specifies */
969 UINT msi_apply_patches( MSIPACKAGE *package )
971 LPWSTR patch_list, *patches;
972 UINT i, r = ERROR_SUCCESS;
974 patch_list = msi_dup_property( package->db, szPatch );
976 TRACE("patches to be applied: %s\n", debugstr_w(patch_list));
978 patches = msi_split_string( patch_list, ';' );
979 for (i = 0; patches && patches[i] && r == ERROR_SUCCESS; i++)
980 r = msi_apply_patch_package( package, patches[i] );
982 msi_free( patches );
983 msi_free( patch_list );
984 return r;
987 UINT msi_apply_transforms( MSIPACKAGE *package )
989 static const WCHAR szTransforms[] = {'T','R','A','N','S','F','O','R','M','S',0};
990 LPWSTR xform_list, *xforms;
991 UINT i, r = ERROR_SUCCESS;
993 xform_list = msi_dup_property( package->db, szTransforms );
994 xforms = msi_split_string( xform_list, ';' );
996 for (i = 0; xforms && xforms[i] && r == ERROR_SUCCESS; i++)
998 if (xforms[i][0] == ':')
999 r = apply_substorage_transform( package, package->db, xforms[i] );
1000 else
1002 WCHAR *transform;
1004 if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
1005 else
1007 WCHAR *p = wcsrchr( package->PackagePath, '\\' );
1008 DWORD len = p - package->PackagePath + 1;
1010 if (!(transform = msi_alloc( (len + lstrlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
1012 msi_free( xforms );
1013 msi_free( xform_list );
1014 return ERROR_OUTOFMEMORY;
1016 memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
1017 memcpy( transform + len, xforms[i], (lstrlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
1019 r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
1020 if (transform != xforms[i]) msi_free( transform );
1023 msi_free( xforms );
1024 msi_free( xform_list );
1025 return r;
1028 UINT msi_apply_registered_patch( MSIPACKAGE *package, LPCWSTR patch_code )
1030 UINT r;
1031 DWORD len;
1032 WCHAR patch_file[MAX_PATH];
1033 MSIDATABASE *patch_db;
1034 MSIPATCHINFO *patch_info;
1035 MSISUMMARYINFO *si;
1037 TRACE("%p, %s\n", package, debugstr_w(patch_code));
1039 len = ARRAY_SIZE( patch_file );
1040 r = MsiGetPatchInfoExW( patch_code, package->ProductCode, NULL, package->Context,
1041 INSTALLPROPERTY_LOCALPACKAGEW, patch_file, &len );
1042 if (r != ERROR_SUCCESS)
1044 ERR("failed to get patch filename %u\n", r);
1045 return r;
1047 r = MSI_OpenDatabaseW( patch_file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
1048 if (r != ERROR_SUCCESS)
1050 ERR("failed to open patch database %s\n", debugstr_w( patch_file ));
1051 return r;
1053 r = msi_get_suminfo( patch_db->storage, 0, &si );
1054 if (r != ERROR_SUCCESS)
1056 msiobj_release( &patch_db->hdr );
1057 return r;
1059 r = msi_parse_patch_summary( si, &patch_info );
1060 msiobj_release( &si->hdr );
1061 if (r != ERROR_SUCCESS)
1063 ERR("failed to parse patch summary %u\n", r);
1064 msiobj_release( &patch_db->hdr );
1065 return r;
1067 patch_info->registered = TRUE;
1068 patch_info->localfile = strdupW( patch_file );
1069 if (!patch_info->localfile)
1071 msiobj_release( &patch_db->hdr );
1072 msi_free_patchinfo( patch_info );
1073 return ERROR_OUTOFMEMORY;
1075 r = msi_apply_patch_db( package, patch_db, patch_info );
1076 msiobj_release( &patch_db->hdr );
1077 if (r != ERROR_SUCCESS)
1079 ERR("failed to apply patch %u\n", r);
1080 msi_free_patchinfo( patch_info );
1082 return r;