xref: /reactos/dll/win32/msi/patch.c (revision f308c6a2)
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2004,2005 Aric Stewart for CodeWeavers
5  * Copyright 2011 Hans Leidekker for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include <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"
31 
32 WINE_DEFAULT_DEBUG_CHANNEL(msi);
33 
34 static BOOL match_language( MSIPACKAGE *package, LANGID langid )
35 {
36     UINT i;
37 
38     if (!package->num_langids || !langid) return TRUE;
39     for (i = 0; i < package->num_langids; i++)
40     {
41         if (package->langids[i] == langid) return TRUE;
42     }
43     return FALSE;
44 }
45 
46 struct transform_desc
47 {
48     WCHAR *product_code_from;
49     WCHAR *product_code_to;
50     WCHAR *version_from;
51     WCHAR *version_to;
52     WCHAR *upgrade_code;
53 };
54 
55 static void free_transform_desc( struct transform_desc *desc )
56 {
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 );
63 }
64 
65 static struct transform_desc *parse_transform_desc( const WCHAR *str )
66 {
67     struct transform_desc *ret;
68     const WCHAR *p = str, *q;
69     UINT len;
70 
71     if (!(ret = msi_alloc_zero( sizeof(*ret) ))) return NULL;
72 
73     q = wcschr( p, '}' );
74     if (*p != '{' || !q) goto error;
75 
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;
80 
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;
87 
88     p = q + 1;
89     q = wcschr( p, '}' );
90     if (*p != '{' || !q) goto error;
91 
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;
96 
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;
103 
104     p = q + 1;
105     q = wcschr( p, '}' );
106     if (*p != '{' || !q) goto error;
107 
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;
112 
113     return ret;
114 
115 error:
116     free_transform_desc( ret );
117     return NULL;
118 }
119 
120 static UINT check_transform_applicable( MSIPACKAGE *package, IStorage *transform )
121 {
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;
130 
131     r = msi_get_suminfo( transform, 0, &si );
132     if (r != ERROR_SUCCESS)
133     {
134         WARN("no summary information!\n");
135         return r;
136     }
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);
140 
141     /* native is not validating platform */
142     wanted_flags &= ~MSITRANSFORM_VALIDATE_PLATFORM;
143 
144     if (wanted_flags & ~supported_flags)
145     {
146         FIXME("unsupported validation flags 0x%04x\n", wanted_flags);
147         msiobj_release( &si->hdr );
148         return ERROR_FUNCTION_FAILED;
149     }
150     if (!(template = msi_suminfo_dup_string( si, PID_TEMPLATE )))
151     {
152         WARN("no template property!\n");
153         msiobj_release( &si->hdr );
154         return ERROR_FUNCTION_FAILED;
155     }
156     TRACE("template property: %s\n", debugstr_w(template));
157     if (!(product = msi_get_suminfo_product( transform )))
158     {
159         WARN("no product property!\n");
160         msi_free( template );
161         msiobj_release( &si->hdr );
162         return ERROR_FUNCTION_FAILED;
163     }
164     TRACE("product property: %s\n", debugstr_w(product));
165     if (!(desc = parse_transform_desc( product )))
166     {
167         msi_free( template );
168         msiobj_release( &si->hdr );
169         return ERROR_FUNCTION_FAILED;
170     }
171     msi_free( product );
172 
173     if (wanted_flags & MSITRANSFORM_VALIDATE_LANGUAGE)
174     {
175         if (!template[0] || ((p = wcschr( template, ';' )) && match_language( package, wcstol( p + 1, NULL, 10 ) )))
176         {
177             valid_flags |= MSITRANSFORM_VALIDATE_LANGUAGE;
178         }
179     }
180     if (wanted_flags & MSITRANSFORM_VALIDATE_PRODUCT)
181     {
182         WCHAR *product_code_installed = msi_dup_property( package->db, L"ProductCode" );
183 
184         if (!product_code_installed)
185         {
186             msi_free( template );
187             free_transform_desc( desc );
188             msiobj_release( &si->hdr );
189             return ERROR_INSTALL_PACKAGE_INVALID;
190         }
191         if (!wcscmp( desc->product_code_from, product_code_installed ))
192         {
193             valid_flags |= MSITRANSFORM_VALIDATE_PRODUCT;
194         }
195         msi_free( product_code_installed );
196     }
197     msi_free( template );
198     if (wanted_flags & MSITRANSFORM_VALIDATE_MAJORVERSION)
199     {
200         WCHAR *product_version_installed = msi_dup_property( package->db, L"ProductVersion" );
201         DWORD major_installed, minor_installed, major, minor;
202 
203         if (!product_version_installed)
204         {
205             free_transform_desc( desc );
206             msiobj_release( &si->hdr );
207             return ERROR_INSTALL_PACKAGE_INVALID;
208         }
209         msi_parse_version_string( product_version_installed, &major_installed, &minor_installed );
210         msi_parse_version_string( desc->version_from, &major, &minor );
211 
212         if (major_installed == major)
213         {
214             valid_flags |= MSITRANSFORM_VALIDATE_MAJORVERSION;
215             wanted_flags &= ~MSITRANSFORM_VALIDATE_MINORVERSION;
216         }
217         msi_free( product_version_installed );
218     }
219     else if (wanted_flags & MSITRANSFORM_VALIDATE_MINORVERSION)
220     {
221         WCHAR *product_version_installed = msi_dup_property( package->db, L"ProductVersion" );
222         DWORD major_installed, minor_installed, major, minor;
223 
224         if (!product_version_installed)
225         {
226             free_transform_desc( desc );
227             msiobj_release( &si->hdr );
228             return ERROR_INSTALL_PACKAGE_INVALID;
229         }
230         msi_parse_version_string( product_version_installed, &major_installed, &minor_installed );
231         msi_parse_version_string( desc->version_from, &major, &minor );
232 
233         if (major_installed == major && minor_installed == minor)
234             valid_flags |= MSITRANSFORM_VALIDATE_MINORVERSION;
235         msi_free( product_version_installed );
236     }
237     if (wanted_flags & MSITRANSFORM_VALIDATE_UPGRADECODE)
238     {
239         WCHAR *upgrade_code_installed = msi_dup_property( package->db, L"UpgradeCode" );
240 
241         if (!upgrade_code_installed)
242         {
243             free_transform_desc( desc );
244             msiobj_release( &si->hdr );
245             return ERROR_INSTALL_PACKAGE_INVALID;
246         }
247         if (!wcscmp( desc->upgrade_code, upgrade_code_installed ))
248             valid_flags |= MSITRANSFORM_VALIDATE_UPGRADECODE;
249         msi_free( upgrade_code_installed );
250     }
251 
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;
257 }
258 
259 static UINT apply_substorage_transform( MSIPACKAGE *package, MSIDATABASE *patch_db, LPCWSTR name )
260 {
261     UINT ret = ERROR_FUNCTION_FAILED;
262     IStorage *stg = NULL;
263     HRESULT r;
264 
265     TRACE("%p %s\n", package, debugstr_w(name));
266 
267     if (*name++ != ':')
268     {
269         ERR("expected a colon in %s\n", debugstr_w(name));
270         return ERROR_FUNCTION_FAILED;
271     }
272     r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
273     if (SUCCEEDED(r))
274     {
275         ret = check_transform_applicable( package, stg );
276         if (ret == ERROR_SUCCESS)
277         {
278             msi_table_apply_transform( package->db, stg, MSITRANSFORM_ERROR_VIEWTRANSFORM );
279             msi_table_apply_transform( package->db, stg, 0 );
280         }
281         else
282         {
283             TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
284         }
285         IStorage_Release( stg );
286     }
287     else
288     {
289         ERR("failed to open substorage %s\n", debugstr_w(name));
290     }
291     return ERROR_SUCCESS;
292 }
293 
294 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
295 {
296     LPWSTR guid_list, *guids, product_code;
297     UINT i, ret = ERROR_FUNCTION_FAILED;
298 
299     product_code = msi_dup_property( package->db, L"ProductCode" );
300     if (!product_code)
301     {
302         /* FIXME: the property ProductCode should be written into the DB somewhere */
303         ERR("no product code to check\n");
304         return ERROR_SUCCESS;
305     }
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++)
309     {
310         if (!wcscmp( guids[i], product_code )) ret = ERROR_SUCCESS;
311     }
312     msi_free( guids );
313     msi_free( guid_list );
314     msi_free( product_code );
315     return ret;
316 }
317 
318 static UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
319 {
320     MSIPATCHINFO *pi;
321     UINT r = ERROR_SUCCESS;
322     WCHAR *p;
323 
324     if (!(pi = msi_alloc_zero( sizeof(MSIPATCHINFO) )))
325     {
326         return ERROR_OUTOFMEMORY;
327     }
328     if (!(pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER )))
329     {
330         msi_free( pi );
331         return ERROR_OUTOFMEMORY;
332     }
333     p = pi->patchcode;
334     if (*p != '{')
335     {
336         msi_free( pi->patchcode );
337         msi_free( pi );
338         return ERROR_PATCH_PACKAGE_INVALID;
339     }
340     if (!(p = wcschr( p + 1, '}' )))
341     {
342         msi_free( pi->patchcode );
343         msi_free( pi );
344         return ERROR_PATCH_PACKAGE_INVALID;
345     }
346     if (p[1])
347     {
348         FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
349         p[1] = 0;
350     }
351     TRACE("patch code %s\n", debugstr_w(pi->patchcode));
352     if (!(pi->products = msi_suminfo_dup_string( si, PID_TEMPLATE )))
353     {
354         msi_free( pi->patchcode );
355         msi_free( pi );
356         return ERROR_OUTOFMEMORY;
357     }
358     if (!(pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR )))
359     {
360         msi_free( pi->patchcode );
361         msi_free( pi->products );
362         msi_free( pi );
363         return ERROR_OUTOFMEMORY;
364     }
365     *patch = pi;
366     return r;
367 }
368 
369 static UINT patch_set_media_source_prop( MSIPACKAGE *package )
370 {
371     MSIQUERY *view;
372     MSIRECORD *rec;
373     const WCHAR *property;
374     WCHAR *patch;
375     UINT r;
376 
377     r = MSI_DatabaseOpenViewW( package->db, L"SELECT `Source` FROM `Media` WHERE `Source` IS NOT NULL", &view );
378     if (r != ERROR_SUCCESS)
379         return r;
380 
381     r = MSI_ViewExecute( view, 0 );
382     if (r != ERROR_SUCCESS)
383         goto done;
384 
385     if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
386     {
387         property = MSI_RecordGetString( rec, 1 );
388         patch = msi_dup_property( package->db, L"PATCH" );
389         msi_set_property( package->db, property, patch, -1 );
390         msi_free( patch );
391         msiobj_release( &rec->hdr );
392     }
393 
394 done:
395     msiobj_release( &view->hdr );
396     return r;
397 }
398 
399 struct patch_offset
400 {
401     struct list entry;
402     WCHAR *name;
403     UINT sequence;
404 };
405 
406 struct patch_offset_list
407 {
408     struct list files;
409     struct list patches;
410     UINT count, min, max;
411     UINT offset_to_apply;
412 };
413 
414 static struct patch_offset_list *patch_offset_list_create( void )
415 {
416     struct patch_offset_list *pos = msi_alloc( sizeof(struct patch_offset_list) );
417     list_init( &pos->files );
418     list_init( &pos->patches );
419     pos->count = pos->max = 0;
420     pos->min = 999999;
421     return pos;
422 }
423 
424 static void patch_offset_list_free( struct patch_offset_list *pos )
425 {
426     struct patch_offset *po, *po2;
427 
428     LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct patch_offset, entry )
429     {
430         msi_free( po->name );
431         msi_free( po );
432     }
433     LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->patches, struct patch_offset, entry )
434     {
435         msi_free( po->name );
436         msi_free( po );
437     }
438     msi_free( pos );
439 }
440 
441 static void patch_offset_get_filepatches( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
442 {
443     MSIQUERY *view;
444     MSIRECORD *rec;
445     UINT r;
446 
447     r = MSI_DatabaseOpenViewW( db, L"SELECT * FROM `Patch` WHERE `Sequence` <= ? ORDER BY `Sequence`", &view );
448     if (r != ERROR_SUCCESS)
449         return;
450 
451     rec = MSI_CreateRecord( 1 );
452     MSI_RecordSetInteger( rec, 1, last_sequence );
453 
454     r = MSI_ViewExecute( view, rec );
455     msiobj_release( &rec->hdr );
456     if (r != ERROR_SUCCESS)
457         return;
458 
459     while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
460     {
461         struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) );
462 
463         po->name     = msi_dup_record_field( rec, 1 );
464         po->sequence = MSI_RecordGetInteger( rec, 2 );
465         pos->min = min( pos->min, po->sequence );
466         pos->max = max( pos->max, po->sequence );
467         list_add_tail( &pos->patches, &po->entry );
468         pos->count++;
469 
470         msiobj_release( &rec->hdr );
471     }
472     msiobj_release( &view->hdr );
473 }
474 
475 static void patch_offset_get_files( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
476 {
477     MSIQUERY *view;
478     MSIRECORD *rec;
479     UINT r;
480 
481     r = MSI_DatabaseOpenViewW( db, L"SELECT * FROM `File` WHERE `Sequence` <= ? ORDER BY `Sequence`", &view );
482     if (r != ERROR_SUCCESS)
483         return;
484 
485     rec = MSI_CreateRecord( 1 );
486     MSI_RecordSetInteger( rec, 1, last_sequence );
487 
488     r = MSI_ViewExecute( view, rec );
489     msiobj_release( &rec->hdr );
490     if (r != ERROR_SUCCESS)
491         return;
492 
493     while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
494     {
495         UINT attributes = MSI_RecordGetInteger( rec, 7 );
496         if (attributes & msidbFileAttributesPatchAdded)
497         {
498             struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) );
499 
500             po->name     = msi_dup_record_field( rec, 1 );
501             po->sequence = MSI_RecordGetInteger( rec, 8 );
502             pos->min     = min( pos->min, po->sequence );
503             pos->max     = max( pos->max, po->sequence );
504             list_add_tail( &pos->files, &po->entry );
505             pos->count++;
506         }
507         msiobj_release( &rec->hdr );
508     }
509     msiobj_release( &view->hdr );
510 }
511 
512 static UINT patch_update_file_sequence( MSIDATABASE *db, const struct patch_offset_list *pos,
513                                         MSIQUERY *view, MSIRECORD *rec )
514 {
515     struct patch_offset *po;
516     const WCHAR *file = MSI_RecordGetString( rec, 1 );
517     UINT r = ERROR_SUCCESS, seq = MSI_RecordGetInteger( rec, 8 );
518 
519     LIST_FOR_EACH_ENTRY( po, &pos->files, struct patch_offset, entry )
520     {
521         if (!wcsicmp( file, po->name ))
522         {
523             MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply );
524             r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
525             if (r != ERROR_SUCCESS)
526                 ERR("Failed to update offset for file %s (%u)\n", debugstr_w(file), r);
527             break;
528         }
529     }
530     return r;
531 }
532 
533 static UINT patch_update_filepatch_sequence( MSIDATABASE *db, const struct patch_offset_list *pos,
534                                              MSIQUERY *view, MSIRECORD *rec )
535 {
536     struct patch_offset *po;
537     const WCHAR *file = MSI_RecordGetString( rec, 1 );
538     UINT r = ERROR_SUCCESS, seq = MSI_RecordGetInteger( rec, 2 );
539 
540     LIST_FOR_EACH_ENTRY( po, &pos->patches, struct patch_offset, entry )
541     {
542         if (seq == po->sequence && !wcsicmp( file, po->name ))
543         {
544             MSIQUERY *delete_view, *insert_view;
545             MSIRECORD *rec2;
546 
547             r = MSI_DatabaseOpenViewW( db, L"DELETE FROM `Patch` WHERE `File_` = ? AND `Sequence` = ?", &delete_view );
548             if (r != ERROR_SUCCESS) return r;
549 
550             rec2 = MSI_CreateRecord( 2 );
551             MSI_RecordSetStringW( rec2, 1, po->name );
552             MSI_RecordSetInteger( rec2, 2, po->sequence );
553             r = MSI_ViewExecute( delete_view, rec2 );
554             msiobj_release( &delete_view->hdr );
555             msiobj_release( &rec2->hdr );
556             if (r != ERROR_SUCCESS) return r;
557 
558             r = MSI_DatabaseOpenViewW( db, L"INSERT INTO `Patch` (`File_`,`Sequence`,`PatchSize`,`Attributes`,"
559                                            L"`Header`,`StreamRef_`) VALUES (?,?,?,?,?,?)", &insert_view );
560             if (r != ERROR_SUCCESS) return r;
561 
562             MSI_RecordSetInteger( rec, 2, po->sequence + pos->offset_to_apply );
563 
564             r = MSI_ViewExecute( insert_view, rec );
565             msiobj_release( &insert_view->hdr );
566             if (r != ERROR_SUCCESS)
567                 ERR("Failed to update offset for filepatch %s (%u)\n", debugstr_w(file), r);
568             break;
569         }
570     }
571     return r;
572 }
573 
574 static UINT patch_offset_modify_db( MSIDATABASE *db, struct patch_offset_list *pos )
575 {
576     MSIRECORD *rec;
577     MSIQUERY *view;
578     UINT r, min = pos->min, max = pos->max, r_fetch;
579 
580     r = MSI_DatabaseOpenViewW( db,
581                                L"SELECT * FROM `File` WHERE `Sequence` >= ? AND `Sequence` <= ? ORDER BY `Sequence`",
582                                &view );
583     if (r != ERROR_SUCCESS)
584         return ERROR_SUCCESS;
585 
586     rec = MSI_CreateRecord( 2 );
587     MSI_RecordSetInteger( rec, 1, min );
588     MSI_RecordSetInteger( rec, 2, max );
589 
590     r = MSI_ViewExecute( view, rec );
591     msiobj_release( &rec->hdr );
592     if (r != ERROR_SUCCESS)
593         goto done;
594 
595     while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
596     {
597         r = patch_update_file_sequence( db, pos, view, rec );
598         msiobj_release( &rec->hdr );
599         if (r != ERROR_SUCCESS) goto done;
600     }
601     msiobj_release( &view->hdr );
602 
603     r = MSI_DatabaseOpenViewW( db,
604                                L"SELECT *FROM `Patch` WHERE `Sequence` >= ? AND `Sequence` <= ? ORDER BY `Sequence`",
605                                &view );
606     if (r != ERROR_SUCCESS)
607         return ERROR_SUCCESS;
608 
609     rec = MSI_CreateRecord( 2 );
610     MSI_RecordSetInteger( rec, 1, min );
611     MSI_RecordSetInteger( rec, 2, max );
612 
613     r = MSI_ViewExecute( view, rec );
614     msiobj_release( &rec->hdr );
615     if (r != ERROR_SUCCESS)
616         goto done;
617 
618     while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
619     {
620         r = patch_update_filepatch_sequence( db, pos, view, rec );
621         msiobj_release( &rec->hdr );
622         if (r != ERROR_SUCCESS) goto done;
623     }
624 
625 done:
626     msiobj_release( &view->hdr );
627     return r;
628 }
629 
630 static const WCHAR patch_media_query[] =
631     L"SELECT * FROM `Media` WHERE `Source` IS NOT NULL AND `Cabinet` IS NOT NULL ORDER BY `DiskId`";
632 
633 struct patch_media
634 {
635     struct list entry;
636     UINT    disk_id;
637     UINT    last_sequence;
638     WCHAR  *prompt;
639     WCHAR  *cabinet;
640     WCHAR  *volume;
641     WCHAR  *source;
642 };
643 
644 static UINT patch_add_media( MSIPACKAGE *package, IStorage *storage, MSIPATCHINFO *patch )
645 {
646     MSIQUERY *view;
647     MSIRECORD *rec;
648     UINT r, disk_id;
649     struct list media_list;
650     struct patch_media *media, *next;
651 
652     r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view );
653     if (r != ERROR_SUCCESS) return r;
654 
655     r = MSI_ViewExecute( view, 0 );
656     if (r != ERROR_SUCCESS)
657     {
658         msiobj_release( &view->hdr );
659         TRACE("query failed %u\n", r);
660         return r;
661     }
662     list_init( &media_list );
663     while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
664     {
665         disk_id = MSI_RecordGetInteger( rec, 1 );
666         TRACE("disk_id %u\n", disk_id);
667         if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
668         {
669             msiobj_release( &rec->hdr );
670             continue;
671         }
672         if (!(media = msi_alloc( sizeof( *media ))))
673         {
674             msiobj_release( &rec->hdr );
675             goto done;
676         }
677         media->disk_id = disk_id;
678         media->last_sequence = MSI_RecordGetInteger( rec, 2 );
679         media->prompt  = msi_dup_record_field( rec, 3 );
680         media->cabinet = msi_dup_record_field( rec, 4 );
681         media->volume  = msi_dup_record_field( rec, 5 );
682         media->source  = msi_dup_record_field( rec, 6 );
683 
684         list_add_tail( &media_list, &media->entry );
685         msiobj_release( &rec->hdr );
686     }
687     LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry )
688     {
689         MSIQUERY *delete_view, *insert_view;
690 
691         r = MSI_DatabaseOpenViewW( package->db, L"DELETE FROM `Media` WHERE `DiskId`=?", &delete_view );
692         if (r != ERROR_SUCCESS) goto done;
693 
694         rec = MSI_CreateRecord( 1 );
695         MSI_RecordSetInteger( rec, 1, media->disk_id );
696 
697         r = MSI_ViewExecute( delete_view, rec );
698         msiobj_release( &delete_view->hdr );
699         msiobj_release( &rec->hdr );
700         if (r != ERROR_SUCCESS) goto done;
701 
702         r = MSI_DatabaseOpenViewW( package->db, L"INSERT INTO `Media` (`DiskId`,`LastSequence`,`DiskPrompt`,"
703                                                 L"`Cabinet`,`VolumeLabel`,`Source`) VALUES (?,?,?,?,?,?)",
704                                   &insert_view );
705         if (r != ERROR_SUCCESS) goto done;
706 
707         disk_id = package->db->media_transform_disk_id;
708         TRACE("disk id       %u\n", disk_id);
709         TRACE("last sequence %u\n", media->last_sequence);
710         TRACE("prompt        %s\n", debugstr_w(media->prompt));
711         TRACE("cabinet       %s\n", debugstr_w(media->cabinet));
712         TRACE("volume        %s\n", debugstr_w(media->volume));
713         TRACE("source        %s\n", debugstr_w(media->source));
714 
715         rec = MSI_CreateRecord( 6 );
716         MSI_RecordSetInteger( rec, 1, disk_id );
717         MSI_RecordSetInteger( rec, 2, media->last_sequence );
718         MSI_RecordSetStringW( rec, 3, media->prompt );
719         MSI_RecordSetStringW( rec, 4, media->cabinet );
720         MSI_RecordSetStringW( rec, 5, media->volume );
721         MSI_RecordSetStringW( rec, 6, media->source );
722 
723         r = MSI_ViewExecute( insert_view, rec );
724         msiobj_release( &insert_view->hdr );
725         msiobj_release( &rec->hdr );
726         if (r != ERROR_SUCCESS) goto done;
727 
728         r = msi_add_cabinet_stream( package, disk_id, storage, media->cabinet );
729         if (r != ERROR_SUCCESS) ERR("failed to add cabinet stream %u\n", r);
730         else
731         {
732             patch->disk_id = disk_id;
733             package->db->media_transform_disk_id++;
734         }
735     }
736 
737 done:
738     msiobj_release( &view->hdr );
739     LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry )
740     {
741         list_remove( &media->entry );
742         msi_free( media->prompt );
743         msi_free( media->cabinet );
744         msi_free( media->volume );
745         msi_free( media->source );
746         msi_free( media );
747     }
748     return r;
749 }
750 
751 static UINT patch_set_offsets( MSIDATABASE *db, MSIPATCHINFO *patch )
752 {
753     MSIQUERY *view;
754     MSIRECORD *rec;
755     UINT r;
756 
757     r = MSI_DatabaseOpenViewW( db, patch_media_query, &view );
758     if (r != ERROR_SUCCESS)
759         return r;
760 
761     r = MSI_ViewExecute( view, 0 );
762     if (r != ERROR_SUCCESS)
763         goto done;
764 
765     while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
766     {
767         UINT offset, last_sequence = MSI_RecordGetInteger( rec, 2 );
768         struct patch_offset_list *pos;
769 
770         /* FIXME: set/check Source field instead? */
771         if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
772         {
773             msiobj_release( &rec->hdr );
774             continue;
775         }
776         pos = patch_offset_list_create();
777         patch_offset_get_files( db, last_sequence, pos );
778         patch_offset_get_filepatches( db, last_sequence, pos );
779 
780         offset = db->media_transform_offset - pos->min;
781         last_sequence = offset + pos->max;
782 
783         last_sequence += pos->min;
784         pos->offset_to_apply = offset;
785         if (pos->count)
786         {
787             r = patch_offset_modify_db( db, pos );
788             if (r != ERROR_SUCCESS)
789                 ERR("Failed to set offsets, expect breakage (%u)\n", r);
790         }
791         MSI_RecordSetInteger( rec, 2, last_sequence );
792         r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
793         if (r != ERROR_SUCCESS)
794             ERR("Failed to update Media table entry, expect breakage (%u)\n", r);
795 
796         db->media_transform_offset = last_sequence + 1;
797 
798         patch_offset_list_free( pos );
799         msiobj_release( &rec->hdr );
800     }
801 
802 done:
803     msiobj_release( &view->hdr );
804     return r;
805 }
806 
807 static DWORD is_uninstallable( MSIDATABASE *db )
808 {
809     MSIQUERY *view;
810     MSIRECORD *rec;
811     DWORD ret = 0;
812 
813     if (MSI_DatabaseOpenViewW( db, L"SELECT `Value` FROM `MsiPatchMetadata` WHERE `Company` IS NULL "
814                                    L"AND `Property`='AllowRemoval'", &view ) != ERROR_SUCCESS) return 0;
815     if (MSI_ViewExecute( view, 0 ) != ERROR_SUCCESS)
816     {
817         msiobj_release( &view->hdr );
818         return 0;
819     }
820 
821     if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
822     {
823         const WCHAR *value = MSI_RecordGetString( rec, 1 );
824         ret = wcstol( value, NULL, 10 );
825         msiobj_release( &rec->hdr );
826     }
827 
828     FIXME( "check other criteria\n" );
829 
830     msiobj_release( &view->hdr );
831     return ret;
832 }
833 
834 static UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
835 {
836     UINT i, r = ERROR_SUCCESS;
837     WCHAR **substorage;
838 
839     /* apply substorage transforms */
840     substorage = msi_split_string( patch->transforms, ';' );
841     for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
842     {
843         r = apply_substorage_transform( package, patch_db, substorage[i] );
844         if (r == ERROR_SUCCESS)
845         {
846             r = patch_set_offsets( package->db, patch );
847             if (r == ERROR_SUCCESS)
848                 r = patch_add_media( package, patch_db->storage, patch );
849         }
850     }
851     msi_free( substorage );
852     if (r != ERROR_SUCCESS)
853         return r;
854 
855     r = patch_set_media_source_prop( package );
856     if (r != ERROR_SUCCESS)
857         return r;
858 
859     patch->uninstallable = is_uninstallable( patch_db );
860     patch->state         = MSIPATCHSTATE_APPLIED;
861     list_add_tail( &package->patches, &patch->entry );
862     return ERROR_SUCCESS;
863 }
864 
865 void msi_free_patchinfo( MSIPATCHINFO *patch )
866 {
867     msi_free( patch->patchcode );
868     msi_free( patch->products );
869     msi_free( patch->transforms );
870     msi_free( patch->filename );
871     msi_free( patch->localfile );
872     msi_free( patch );
873 }
874 
875 static UINT msi_apply_patch_package( MSIPACKAGE *package, const WCHAR *file )
876 {
877     MSIDATABASE *patch_db = NULL;
878     WCHAR localfile[MAX_PATH];
879     MSISUMMARYINFO *si;
880     MSIPATCHINFO *patch = NULL;
881     UINT r;
882 
883     TRACE("%p, %s\n", package, debugstr_w(file));
884 
885     r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
886     if (r != ERROR_SUCCESS)
887     {
888         ERR("failed to open patch collection %s\n", debugstr_w( file ) );
889         return r;
890     }
891     r = msi_get_suminfo( patch_db->storage, 0, &si );
892     if (r != ERROR_SUCCESS)
893     {
894         msiobj_release( &patch_db->hdr );
895         return r;
896     }
897     r = msi_check_patch_applicable( package, si );
898     if (r != ERROR_SUCCESS)
899     {
900         TRACE("patch not applicable\n");
901         r = ERROR_SUCCESS;
902         goto done;
903     }
904     r = msi_parse_patch_summary( si, &patch );
905     if ( r != ERROR_SUCCESS )
906         goto done;
907 
908     r = msi_create_empty_local_file( localfile, L".msp" );
909     if ( r != ERROR_SUCCESS )
910         goto done;
911 
912     r = ERROR_OUTOFMEMORY;
913     patch->registered = FALSE;
914     if (!(patch->filename = strdupW( file ))) goto done;
915     if (!(patch->localfile = strdupW( localfile ))) goto done;
916 
917     r = msi_apply_patch_db( package, patch_db, patch );
918     if (r != ERROR_SUCCESS) WARN("patch failed to apply %u\n", r);
919 
920 done:
921     msiobj_release( &si->hdr );
922     msiobj_release( &patch_db->hdr );
923     if (patch && r != ERROR_SUCCESS)
924     {
925         DeleteFileW( patch->localfile );
926         msi_free_patchinfo( patch );
927     }
928     return r;
929 }
930 
931 /* get the PATCH property, and apply all the patches it specifies */
932 UINT msi_apply_patches( MSIPACKAGE *package )
933 {
934     LPWSTR patch_list, *patches;
935     UINT i, r = ERROR_SUCCESS;
936 
937     patch_list = msi_dup_property( package->db, L"PATCH" );
938 
939     TRACE("patches to be applied: %s\n", debugstr_w(patch_list));
940 
941     patches = msi_split_string( patch_list, ';' );
942     for (i = 0; patches && patches[i] && r == ERROR_SUCCESS; i++)
943         r = msi_apply_patch_package( package, patches[i] );
944 
945     msi_free( patches );
946     msi_free( patch_list );
947     return r;
948 }
949 
950 UINT msi_apply_transforms( MSIPACKAGE *package )
951 {
952     LPWSTR xform_list, *xforms;
953     UINT i, r = ERROR_SUCCESS;
954 
955     xform_list = msi_dup_property( package->db, L"TRANSFORMS" );
956     xforms = msi_split_string( xform_list, ';' );
957 
958     for (i = 0; xforms && xforms[i] && r == ERROR_SUCCESS; i++)
959     {
960         if (xforms[i][0] == ':')
961             r = apply_substorage_transform( package, package->db, xforms[i] );
962         else
963         {
964             WCHAR *transform;
965 
966             if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
967             else
968             {
969                 WCHAR *p = wcsrchr( package->PackagePath, '\\' );
970                 DWORD len = p - package->PackagePath + 1;
971 
972                 if (!(transform = msi_alloc( (len + lstrlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
973                 {
974                     msi_free( xforms );
975                     msi_free( xform_list );
976                     return ERROR_OUTOFMEMORY;
977                 }
978                 memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
979                 memcpy( transform + len, xforms[i], (lstrlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
980             }
981             r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
982             if (transform != xforms[i]) msi_free( transform );
983         }
984     }
985     msi_free( xforms );
986     msi_free( xform_list );
987     return r;
988 }
989 
990 UINT msi_apply_registered_patch( MSIPACKAGE *package, LPCWSTR patch_code )
991 {
992     UINT r;
993     DWORD len;
994     WCHAR patch_file[MAX_PATH];
995     MSIDATABASE *patch_db;
996     MSIPATCHINFO *patch_info;
997     MSISUMMARYINFO *si;
998 
999     TRACE("%p, %s\n", package, debugstr_w(patch_code));
1000 
1001     len = ARRAY_SIZE( patch_file );
1002     r = MsiGetPatchInfoExW( patch_code, package->ProductCode, NULL, package->Context,
1003                             INSTALLPROPERTY_LOCALPACKAGEW, patch_file, &len );
1004     if (r != ERROR_SUCCESS)
1005     {
1006         ERR("failed to get patch filename %u\n", r);
1007         return r;
1008     }
1009     r = MSI_OpenDatabaseW( patch_file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
1010     if (r != ERROR_SUCCESS)
1011     {
1012         ERR("failed to open patch database %s\n", debugstr_w( patch_file ));
1013         return r;
1014     }
1015     r = msi_get_suminfo( patch_db->storage, 0, &si );
1016     if (r != ERROR_SUCCESS)
1017     {
1018         msiobj_release( &patch_db->hdr );
1019         return r;
1020     }
1021     r = msi_parse_patch_summary( si, &patch_info );
1022     msiobj_release( &si->hdr );
1023     if (r != ERROR_SUCCESS)
1024     {
1025         ERR("failed to parse patch summary %u\n", r);
1026         msiobj_release( &patch_db->hdr );
1027         return r;
1028     }
1029     patch_info->registered = TRUE;
1030     patch_info->localfile = strdupW( patch_file );
1031     if (!patch_info->localfile)
1032     {
1033         msiobj_release( &patch_db->hdr );
1034         msi_free_patchinfo( patch_info );
1035         return ERROR_OUTOFMEMORY;
1036     }
1037     r = msi_apply_patch_db( package, patch_db, patch_info );
1038     msiobj_release( &patch_db->hdr );
1039     if (r != ERROR_SUCCESS)
1040     {
1041         ERR("failed to apply patch %u\n", r);
1042         msi_free_patchinfo( patch_info );
1043     }
1044     return r;
1045 }
1046