xref: /reactos/dll/win32/msi/patch.c (revision 50cf16b3)
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 "wine/unicode.h"
31 #include "msipriv.h"
32 
33 WINE_DEFAULT_DEBUG_CHANNEL(msi);
34 
35 static BOOL match_language( MSIPACKAGE *package, LANGID langid )
36 {
37     UINT i;
38 
39     if (!package->num_langids || !langid) return TRUE;
40     for (i = 0; i < package->num_langids; i++)
41     {
42         if (package->langids[i] == langid) return TRUE;
43     }
44     return FALSE;
45 }
46 
47 struct transform_desc
48 {
49     WCHAR *product_code_from;
50     WCHAR *product_code_to;
51     WCHAR *version_from;
52     WCHAR *version_to;
53     WCHAR *upgrade_code;
54 };
55 
56 static void free_transform_desc( struct transform_desc *desc )
57 {
58     msi_free( desc->product_code_from );
59     msi_free( desc->product_code_to );
60     msi_free( desc->version_from );
61     msi_free( desc->version_to );
62     msi_free( desc->upgrade_code );
63     msi_free( desc );
64 }
65 
66 static struct transform_desc *parse_transform_desc( const WCHAR *str )
67 {
68     struct transform_desc *ret;
69     const WCHAR *p = str, *q;
70     UINT len;
71 
72     if (!(ret = msi_alloc_zero( sizeof(*ret) ))) return NULL;
73 
74     q = strchrW( p, '}' );
75     if (*p != '{' || !q) goto error;
76 
77     len = q - p + 1;
78     if (!(ret->product_code_from = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error;
79     memcpy( ret->product_code_from, p, len * sizeof(WCHAR) );
80     ret->product_code_from[len] = 0;
81 
82     p = q + 1;
83     if (!(q = strchrW( p, ';' ))) goto error;
84     len = q - p;
85     if (!(ret->version_from = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error;
86     memcpy( ret->version_from, p, len * sizeof(WCHAR) );
87     ret->version_from[len] = 0;
88 
89     p = q + 1;
90     q = strchrW( p, '}' );
91     if (*p != '{' || !q) goto error;
92 
93     len = q - p + 1;
94     if (!(ret->product_code_to = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error;
95     memcpy( ret->product_code_to, p, len * sizeof(WCHAR) );
96     ret->product_code_to[len] = 0;
97 
98     p = q + 1;
99     if (!(q = strchrW( p, ';' ))) goto error;
100     len = q - p;
101     if (!(ret->version_to = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error;
102     memcpy( ret->version_to, p, len * sizeof(WCHAR) );
103     ret->version_to[len] = 0;
104 
105     p = q + 1;
106     q = strchrW( p, '}' );
107     if (*p != '{' || !q) goto error;
108 
109     len = q - p + 1;
110     if (!(ret->upgrade_code = msi_alloc( (len + 1) * sizeof(WCHAR) ))) goto error;
111     memcpy( ret->upgrade_code, p, len * sizeof(WCHAR) );
112     ret->upgrade_code[len] = 0;
113 
114     return ret;
115 
116 error:
117     free_transform_desc( ret );
118     return NULL;
119 }
120 
121 static UINT check_transform_applicable( MSIPACKAGE *package, IStorage *transform )
122 {
123     static const UINT supported_flags =
124         MSITRANSFORM_VALIDATE_PRODUCT  | MSITRANSFORM_VALIDATE_LANGUAGE |
125         MSITRANSFORM_VALIDATE_PLATFORM | MSITRANSFORM_VALIDATE_MAJORVERSION |
126         MSITRANSFORM_VALIDATE_MINORVERSION | MSITRANSFORM_VALIDATE_UPGRADECODE;
127     MSISUMMARYINFO *si;
128     UINT r, valid_flags = 0, wanted_flags = 0;
129     WCHAR *template, *product, *p;
130     struct transform_desc *desc;
131 
132     r = msi_get_suminfo( transform, 0, &si );
133     if (r != ERROR_SUCCESS)
134     {
135         WARN("no summary information!\n");
136         return r;
137     }
138     wanted_flags = msi_suminfo_get_int32( si, PID_CHARCOUNT );
139     wanted_flags &= 0xffff; /* mask off error condition flags */
140     TRACE("validation flags 0x%04x\n", wanted_flags);
141 
142     /* native is not validating platform */
143     wanted_flags &= ~MSITRANSFORM_VALIDATE_PLATFORM;
144 
145     if (wanted_flags & ~supported_flags)
146     {
147         FIXME("unsupported validation flags 0x%04x\n", wanted_flags);
148         msiobj_release( &si->hdr );
149         return ERROR_FUNCTION_FAILED;
150     }
151     if (!(template = msi_suminfo_dup_string( si, PID_TEMPLATE )))
152     {
153         WARN("no template property!\n");
154         msiobj_release( &si->hdr );
155         return ERROR_FUNCTION_FAILED;
156     }
157     TRACE("template property: %s\n", debugstr_w(template));
158     if (!(product = msi_get_suminfo_product( transform )))
159     {
160         WARN("no product property!\n");
161         msi_free( template );
162         msiobj_release( &si->hdr );
163         return ERROR_FUNCTION_FAILED;
164     }
165     TRACE("product property: %s\n", debugstr_w(product));
166     if (!(desc = parse_transform_desc( product )))
167     {
168         msi_free( template );
169         msiobj_release( &si->hdr );
170         return ERROR_FUNCTION_FAILED;
171     }
172     msi_free( product );
173 
174     if (wanted_flags & MSITRANSFORM_VALIDATE_LANGUAGE)
175     {
176         if (!template[0] || ((p = strchrW( template, ';' )) && match_language( package, atoiW( p + 1 ) )))
177         {
178             valid_flags |= MSITRANSFORM_VALIDATE_LANGUAGE;
179         }
180     }
181     if (wanted_flags & MSITRANSFORM_VALIDATE_PRODUCT)
182     {
183         WCHAR *product_code_installed = msi_dup_property( package->db, szProductCode );
184 
185         if (!product_code_installed)
186         {
187             msi_free( template );
188             free_transform_desc( desc );
189             msiobj_release( &si->hdr );
190             return ERROR_INSTALL_PACKAGE_INVALID;
191         }
192         if (!strcmpW( desc->product_code_from, product_code_installed ))
193         {
194             valid_flags |= MSITRANSFORM_VALIDATE_PRODUCT;
195         }
196         msi_free( product_code_installed );
197     }
198     msi_free( template );
199     if (wanted_flags & MSITRANSFORM_VALIDATE_MAJORVERSION)
200     {
201         WCHAR *product_version_installed = msi_dup_property( package->db, szProductVersion );
202         DWORD major_installed, minor_installed, major, minor;
203 
204         if (!product_version_installed)
205         {
206             free_transform_desc( desc );
207             msiobj_release( &si->hdr );
208             return ERROR_INSTALL_PACKAGE_INVALID;
209         }
210         msi_parse_version_string( product_version_installed, &major_installed, &minor_installed );
211         msi_parse_version_string( desc->version_from, &major, &minor );
212 
213         if (major_installed == major)
214         {
215             valid_flags |= MSITRANSFORM_VALIDATE_MAJORVERSION;
216             wanted_flags &= ~MSITRANSFORM_VALIDATE_MINORVERSION;
217         }
218         msi_free( product_version_installed );
219     }
220     else if (wanted_flags & MSITRANSFORM_VALIDATE_MINORVERSION)
221     {
222         WCHAR *product_version_installed = msi_dup_property( package->db, szProductVersion );
223         DWORD major_installed, minor_installed, major, minor;
224 
225         if (!product_version_installed)
226         {
227             free_transform_desc( desc );
228             msiobj_release( &si->hdr );
229             return ERROR_INSTALL_PACKAGE_INVALID;
230         }
231         msi_parse_version_string( product_version_installed, &major_installed, &minor_installed );
232         msi_parse_version_string( desc->version_from, &major, &minor );
233 
234         if (major_installed == major && minor_installed == minor)
235             valid_flags |= MSITRANSFORM_VALIDATE_MINORVERSION;
236         msi_free( product_version_installed );
237     }
238     if (wanted_flags & MSITRANSFORM_VALIDATE_UPGRADECODE)
239     {
240         WCHAR *upgrade_code_installed = msi_dup_property( package->db, szUpgradeCode );
241 
242         if (!upgrade_code_installed)
243         {
244             free_transform_desc( desc );
245             msiobj_release( &si->hdr );
246             return ERROR_INSTALL_PACKAGE_INVALID;
247         }
248         if (!strcmpW( desc->upgrade_code, upgrade_code_installed ))
249             valid_flags |= MSITRANSFORM_VALIDATE_UPGRADECODE;
250         msi_free( upgrade_code_installed );
251     }
252 
253     free_transform_desc( desc );
254     msiobj_release( &si->hdr );
255     if ((valid_flags & wanted_flags) != wanted_flags) return ERROR_FUNCTION_FAILED;
256     TRACE("applicable transform\n");
257     return ERROR_SUCCESS;
258 }
259 
260 static UINT apply_substorage_transform( MSIPACKAGE *package, MSIDATABASE *patch_db, LPCWSTR name )
261 {
262     UINT ret = ERROR_FUNCTION_FAILED;
263     IStorage *stg = NULL;
264     HRESULT r;
265 
266     TRACE("%p %s\n", package, debugstr_w(name));
267 
268     if (*name++ != ':')
269     {
270         ERR("expected a colon in %s\n", debugstr_w(name));
271         return ERROR_FUNCTION_FAILED;
272     }
273     r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
274     if (SUCCEEDED(r))
275     {
276         ret = check_transform_applicable( package, stg );
277         if (ret == ERROR_SUCCESS)
278             msi_table_apply_transform( package->db, stg );
279         else
280             TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
281         IStorage_Release( stg );
282     }
283     else
284     {
285         ERR("failed to open substorage %s\n", debugstr_w(name));
286     }
287     return ERROR_SUCCESS;
288 }
289 
290 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
291 {
292     LPWSTR guid_list, *guids, product_code;
293     UINT i, ret = ERROR_FUNCTION_FAILED;
294 
295     product_code = msi_dup_property( package->db, szProductCode );
296     if (!product_code)
297     {
298         /* FIXME: the property ProductCode should be written into the DB somewhere */
299         ERR("no product code to check\n");
300         return ERROR_SUCCESS;
301     }
302     guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
303     guids = msi_split_string( guid_list, ';' );
304     for (i = 0; guids[i] && ret != ERROR_SUCCESS; i++)
305     {
306         if (!strcmpW( guids[i], product_code )) ret = ERROR_SUCCESS;
307     }
308     msi_free( guids );
309     msi_free( guid_list );
310     msi_free( product_code );
311     return ret;
312 }
313 
314 static UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
315 {
316     MSIPATCHINFO *pi;
317     UINT r = ERROR_SUCCESS;
318     WCHAR *p;
319 
320     if (!(pi = msi_alloc_zero( sizeof(MSIPATCHINFO) )))
321     {
322         return ERROR_OUTOFMEMORY;
323     }
324     if (!(pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER )))
325     {
326         msi_free( pi );
327         return ERROR_OUTOFMEMORY;
328     }
329     p = pi->patchcode;
330     if (*p != '{')
331     {
332         msi_free( pi->patchcode );
333         msi_free( pi );
334         return ERROR_PATCH_PACKAGE_INVALID;
335     }
336     if (!(p = strchrW( p + 1, '}' )))
337     {
338         msi_free( pi->patchcode );
339         msi_free( pi );
340         return ERROR_PATCH_PACKAGE_INVALID;
341     }
342     if (p[1])
343     {
344         FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
345         p[1] = 0;
346     }
347     TRACE("patch code %s\n", debugstr_w(pi->patchcode));
348     if (!(pi->products = msi_suminfo_dup_string( si, PID_TEMPLATE )))
349     {
350         msi_free( pi->patchcode );
351         msi_free( pi );
352         return ERROR_OUTOFMEMORY;
353     }
354     if (!(pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR )))
355     {
356         msi_free( pi->patchcode );
357         msi_free( pi->products );
358         msi_free( pi );
359         return ERROR_OUTOFMEMORY;
360     }
361     *patch = pi;
362     return r;
363 }
364 
365 static UINT patch_set_media_source_prop( MSIPACKAGE *package )
366 {
367     static const WCHAR query[] = {
368         'S','E','L','E','C','T',' ','`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
369         '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ',
370         'I','S',' ','N','O','T',' ','N','U','L','L',0};
371     MSIQUERY *view;
372     MSIRECORD *rec;
373     const WCHAR *property;
374     WCHAR *patch;
375     UINT r;
376 
377     r = MSI_DatabaseOpenViewW( package->db, query, &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, szPatch );
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     static const WCHAR query[] = {
444         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
445         'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
446         'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
447     MSIQUERY *view;
448     MSIRECORD *rec;
449     UINT r;
450 
451     r = MSI_DatabaseOpenViewW( db, query, &view );
452     if (r != ERROR_SUCCESS)
453         return;
454 
455     rec = MSI_CreateRecord( 1 );
456     MSI_RecordSetInteger( rec, 1, last_sequence );
457 
458     r = MSI_ViewExecute( view, rec );
459     msiobj_release( &rec->hdr );
460     if (r != ERROR_SUCCESS)
461         return;
462 
463     while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
464     {
465         struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) );
466 
467         po->name     = msi_dup_record_field( rec, 1 );
468         po->sequence = MSI_RecordGetInteger( rec, 2 );
469         pos->min = min( pos->min, po->sequence );
470         pos->max = max( pos->max, po->sequence );
471         list_add_tail( &pos->patches, &po->entry );
472         pos->count++;
473 
474         msiobj_release( &rec->hdr );
475     }
476     msiobj_release( &view->hdr );
477 }
478 
479 static void patch_offset_get_files( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
480 {
481     static const WCHAR query[] = {
482         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
483         'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
484         'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
485     MSIQUERY *view;
486     MSIRECORD *rec;
487     UINT r;
488 
489     r = MSI_DatabaseOpenViewW( db, query, &view );
490     if (r != ERROR_SUCCESS)
491         return;
492 
493     rec = MSI_CreateRecord( 1 );
494     MSI_RecordSetInteger( rec, 1, last_sequence );
495 
496     r = MSI_ViewExecute( view, rec );
497     msiobj_release( &rec->hdr );
498     if (r != ERROR_SUCCESS)
499         return;
500 
501     while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
502     {
503         UINT attributes = MSI_RecordGetInteger( rec, 7 );
504         if (attributes & msidbFileAttributesPatchAdded)
505         {
506             struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) );
507 
508             po->name     = msi_dup_record_field( rec, 1 );
509             po->sequence = MSI_RecordGetInteger( rec, 8 );
510             pos->min     = min( pos->min, po->sequence );
511             pos->max     = max( pos->max, po->sequence );
512             list_add_tail( &pos->files, &po->entry );
513             pos->count++;
514         }
515         msiobj_release( &rec->hdr );
516     }
517     msiobj_release( &view->hdr );
518 }
519 
520 static UINT patch_update_file_sequence( MSIDATABASE *db, const struct patch_offset_list *pos,
521                                         MSIQUERY *view, MSIRECORD *rec )
522 {
523     struct patch_offset *po;
524     const WCHAR *file = MSI_RecordGetString( rec, 1 );
525     UINT r = ERROR_SUCCESS, seq = MSI_RecordGetInteger( rec, 8 );
526 
527     LIST_FOR_EACH_ENTRY( po, &pos->files, struct patch_offset, entry )
528     {
529         if (!strcmpiW( file, po->name ))
530         {
531             MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply );
532             r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
533             if (r != ERROR_SUCCESS)
534                 ERR("Failed to update offset for file %s (%u)\n", debugstr_w(file), r);
535             break;
536         }
537     }
538     return r;
539 }
540 
541 static UINT patch_update_filepatch_sequence( MSIDATABASE *db, const struct patch_offset_list *pos,
542                                              MSIQUERY *view, MSIRECORD *rec )
543 {
544     static const WCHAR delete_query[] = {
545         'D','E','L','E','T','E',' ','F','R','O','M',' ','`','P','a','t','c','h','`',' ',
546         'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','?',' ',
547         'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','=',' ','?',0};
548     static const WCHAR insert_query[] = {
549         'I','N','S','E','R','T',' ','I','N','T','O',' ','`','P','a','t','c','h','`',' ',
550         '(','`','F','i','l','e','_','`',',','`','S','e','q','u','e','n','c','e','`',',',
551         '`','P','a','t','c','h','S','i','z','e','`',',','`','A','t','t','r','i','b','u','t','e','s','`',',',
552         '`','H','e','a','d','e','r','`',',','`','S','t','r','e','a','m','R','e','f','_','`',')',' ',
553         'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
554     struct patch_offset *po;
555     const WCHAR *file = MSI_RecordGetString( rec, 1 );
556     UINT r = ERROR_SUCCESS, seq = MSI_RecordGetInteger( rec, 2 );
557 
558     LIST_FOR_EACH_ENTRY( po, &pos->patches, struct patch_offset, entry )
559     {
560         if (seq == po->sequence && !strcmpiW( file, po->name ))
561         {
562             MSIQUERY *delete_view, *insert_view;
563             MSIRECORD *rec2;
564 
565             r = MSI_DatabaseOpenViewW( db, delete_query, &delete_view );
566             if (r != ERROR_SUCCESS) return r;
567 
568             rec2 = MSI_CreateRecord( 2 );
569             MSI_RecordSetStringW( rec2, 1, po->name );
570             MSI_RecordSetInteger( rec2, 2, po->sequence );
571             r = MSI_ViewExecute( delete_view, rec2 );
572             msiobj_release( &delete_view->hdr );
573             msiobj_release( &rec2->hdr );
574             if (r != ERROR_SUCCESS) return r;
575 
576             r = MSI_DatabaseOpenViewW( db, insert_query, &insert_view );
577             if (r != ERROR_SUCCESS) return r;
578 
579             MSI_RecordSetInteger( rec, 2, po->sequence + pos->offset_to_apply );
580 
581             r = MSI_ViewExecute( insert_view, rec );
582             msiobj_release( &insert_view->hdr );
583             if (r != ERROR_SUCCESS)
584                 ERR("Failed to update offset for filepatch %s (%u)\n", debugstr_w(file), r);
585             break;
586         }
587     }
588     return r;
589 }
590 
591 static UINT patch_offset_modify_db( MSIDATABASE *db, struct patch_offset_list *pos )
592 {
593     static const WCHAR file_query[] = {
594         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','i','l','e','`',' ',
595         'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>','=',' ','?',' ',
596         'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','<','=',' ','?',' ',
597         'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
598     static const WCHAR patch_query[] = {
599         'S','E','L','E','C','T',' ','*','F','R','O','M',' ','`','P','a','t','c','h','`',' ',
600         'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>','=',' ','?',' ',
601         'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','<','=',' ','?',' ',
602         'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
603     MSIRECORD *rec;
604     MSIQUERY *view;
605     UINT r, min = pos->min, max = pos->max, r_fetch;
606 
607     r = MSI_DatabaseOpenViewW( db, file_query, &view );
608     if (r != ERROR_SUCCESS)
609         return ERROR_SUCCESS;
610 
611     rec = MSI_CreateRecord( 2 );
612     MSI_RecordSetInteger( rec, 1, min );
613     MSI_RecordSetInteger( rec, 2, max );
614 
615     r = MSI_ViewExecute( view, rec );
616     msiobj_release( &rec->hdr );
617     if (r != ERROR_SUCCESS)
618         goto done;
619 
620     while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
621     {
622         r = patch_update_file_sequence( db, pos, view, rec );
623         msiobj_release( &rec->hdr );
624         if (r != ERROR_SUCCESS) goto done;
625     }
626     msiobj_release( &view->hdr );
627 
628     r = MSI_DatabaseOpenViewW( db, patch_query, &view );
629     if (r != ERROR_SUCCESS)
630         return ERROR_SUCCESS;
631 
632     rec = MSI_CreateRecord( 2 );
633     MSI_RecordSetInteger( rec, 1, min );
634     MSI_RecordSetInteger( rec, 2, max );
635 
636     r = MSI_ViewExecute( view, rec );
637     msiobj_release( &rec->hdr );
638     if (r != ERROR_SUCCESS)
639         goto done;
640 
641     while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
642     {
643         r = patch_update_filepatch_sequence( db, pos, view, rec );
644         msiobj_release( &rec->hdr );
645         if (r != ERROR_SUCCESS) goto done;
646     }
647 
648 done:
649     msiobj_release( &view->hdr );
650     return r;
651 }
652 
653 static const WCHAR patch_media_query[] = {
654     'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
655     'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
656     'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
657     'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
658 
659 struct patch_media
660 {
661     struct list entry;
662     UINT    disk_id;
663     UINT    last_sequence;
664     WCHAR  *prompt;
665     WCHAR  *cabinet;
666     WCHAR  *volume;
667     WCHAR  *source;
668 };
669 
670 static UINT patch_add_media( MSIPACKAGE *package, IStorage *storage, MSIPATCHINFO *patch )
671 {
672     static const WCHAR delete_query[] = {
673         'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
674         'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0};
675     static const WCHAR insert_query[] = {
676         'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ',
677         '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',',
678         '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',',
679         '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ',
680         'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
681     MSIQUERY *view;
682     MSIRECORD *rec;
683     UINT r, disk_id;
684     struct list media_list;
685     struct patch_media *media, *next;
686 
687     r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view );
688     if (r != ERROR_SUCCESS) return r;
689 
690     r = MSI_ViewExecute( view, 0 );
691     if (r != ERROR_SUCCESS)
692     {
693         msiobj_release( &view->hdr );
694         TRACE("query failed %u\n", r);
695         return r;
696     }
697     list_init( &media_list );
698     while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
699     {
700         disk_id = MSI_RecordGetInteger( rec, 1 );
701         TRACE("disk_id %u\n", disk_id);
702         if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
703         {
704             msiobj_release( &rec->hdr );
705             continue;
706         }
707         if (!(media = msi_alloc( sizeof( *media )))) {
708             msiobj_release( &rec->hdr );
709             goto done;
710 	}
711         media->disk_id = disk_id;
712         media->last_sequence = MSI_RecordGetInteger( rec, 2 );
713         media->prompt  = msi_dup_record_field( rec, 3 );
714         media->cabinet = msi_dup_record_field( rec, 4 );
715         media->volume  = msi_dup_record_field( rec, 5 );
716         media->source  = msi_dup_record_field( rec, 6 );
717 
718         list_add_tail( &media_list, &media->entry );
719         msiobj_release( &rec->hdr );
720     }
721     LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry )
722     {
723         MSIQUERY *delete_view, *insert_view;
724 
725         r = MSI_DatabaseOpenViewW( package->db, delete_query, &delete_view );
726         if (r != ERROR_SUCCESS) goto done;
727 
728         rec = MSI_CreateRecord( 1 );
729         MSI_RecordSetInteger( rec, 1, media->disk_id );
730 
731         r = MSI_ViewExecute( delete_view, rec );
732         msiobj_release( &delete_view->hdr );
733         msiobj_release( &rec->hdr );
734         if (r != ERROR_SUCCESS) goto done;
735 
736         r = MSI_DatabaseOpenViewW( package->db, insert_query, &insert_view );
737         if (r != ERROR_SUCCESS) goto done;
738 
739         disk_id = package->db->media_transform_disk_id;
740         TRACE("disk id       %u\n", disk_id);
741         TRACE("last sequence %u\n", media->last_sequence);
742         TRACE("prompt        %s\n", debugstr_w(media->prompt));
743         TRACE("cabinet       %s\n", debugstr_w(media->cabinet));
744         TRACE("volume        %s\n", debugstr_w(media->volume));
745         TRACE("source        %s\n", debugstr_w(media->source));
746 
747         rec = MSI_CreateRecord( 6 );
748         MSI_RecordSetInteger( rec, 1, disk_id );
749         MSI_RecordSetInteger( rec, 2, media->last_sequence );
750         MSI_RecordSetStringW( rec, 3, media->prompt );
751         MSI_RecordSetStringW( rec, 4, media->cabinet );
752         MSI_RecordSetStringW( rec, 5, media->volume );
753         MSI_RecordSetStringW( rec, 6, media->source );
754 
755         r = MSI_ViewExecute( insert_view, rec );
756         msiobj_release( &insert_view->hdr );
757         msiobj_release( &rec->hdr );
758         if (r != ERROR_SUCCESS) goto done;
759 
760         r = msi_add_cabinet_stream( package, disk_id, storage, media->cabinet );
761         if (r != ERROR_SUCCESS) ERR("failed to add cabinet stream %u\n", r);
762         else
763         {
764             patch->disk_id = disk_id;
765             package->db->media_transform_disk_id++;
766         }
767     }
768 
769 done:
770     msiobj_release( &view->hdr );
771     LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry )
772     {
773         list_remove( &media->entry );
774         msi_free( media->prompt );
775         msi_free( media->cabinet );
776         msi_free( media->volume );
777         msi_free( media->source );
778         msi_free( media );
779     }
780     return r;
781 }
782 
783 static UINT patch_set_offsets( MSIDATABASE *db, MSIPATCHINFO *patch )
784 {
785     MSIQUERY *view;
786     MSIRECORD *rec;
787     UINT r;
788 
789     r = MSI_DatabaseOpenViewW( db, patch_media_query, &view );
790     if (r != ERROR_SUCCESS)
791         return r;
792 
793     r = MSI_ViewExecute( view, 0 );
794     if (r != ERROR_SUCCESS)
795         goto done;
796 
797     while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
798     {
799         UINT offset, last_sequence = MSI_RecordGetInteger( rec, 2 );
800         struct patch_offset_list *pos;
801 
802         /* FIXME: set/check Source field instead? */
803         if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
804         {
805             msiobj_release( &rec->hdr );
806             continue;
807         }
808         pos = patch_offset_list_create();
809         patch_offset_get_files( db, last_sequence, pos );
810         patch_offset_get_filepatches( db, last_sequence, pos );
811 
812         offset = db->media_transform_offset - pos->min;
813         last_sequence = offset + pos->max;
814 
815         last_sequence += pos->min;
816         pos->offset_to_apply = offset;
817         if (pos->count)
818         {
819             r = patch_offset_modify_db( db, pos );
820             if (r != ERROR_SUCCESS)
821                 ERR("Failed to set offsets, expect breakage (%u)\n", r);
822         }
823         MSI_RecordSetInteger( rec, 2, last_sequence );
824         r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
825         if (r != ERROR_SUCCESS)
826             ERR("Failed to update Media table entry, expect breakage (%u)\n", r);
827 
828         db->media_transform_offset = last_sequence + 1;
829 
830         patch_offset_list_free( pos );
831         msiobj_release( &rec->hdr );
832     }
833 
834 done:
835     msiobj_release( &view->hdr );
836     return r;
837 }
838 
839 static DWORD is_uninstallable( MSIDATABASE *db )
840 {
841     static const WCHAR query[] = {
842         'S','E','L','E','C','T',' ','`','V','a','l','u','e','`',' ','F','R','O','M',' ',
843         '`','M','s','i','P','a','t','c','h','M','e','t','a','d','a','t','a','`',' ',
844         'W','H','E','R','E',' ','`','C','o','m','p','a','n','y','`',' ','I','S',' ',
845         'N','U','L','L',' ','A','N','D',' ','`','P','r','o','p','e','r','t','y','`','=',
846         '\'','A','l','l','o','w','R','e','m','o','v','a','l','\'',0};
847     MSIQUERY *view;
848     MSIRECORD *rec;
849     DWORD ret = 0;
850 
851     if (MSI_DatabaseOpenViewW( db, query, &view ) != ERROR_SUCCESS) return 0;
852     if (MSI_ViewExecute( view, 0 ) != ERROR_SUCCESS)
853     {
854         msiobj_release( &view->hdr );
855         return 0;
856     }
857 
858     if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
859     {
860         const WCHAR *value = MSI_RecordGetString( rec, 1 );
861         ret = atoiW( value );
862         msiobj_release( &rec->hdr );
863     }
864 
865     FIXME( "check other criteria\n" );
866 
867     msiobj_release( &view->hdr );
868     return ret;
869 }
870 
871 static UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
872 {
873     UINT i, r = ERROR_SUCCESS;
874     WCHAR **substorage;
875 
876     /* apply substorage transforms */
877     substorage = msi_split_string( patch->transforms, ';' );
878     for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
879     {
880         r = apply_substorage_transform( package, patch_db, substorage[i] );
881         if (r == ERROR_SUCCESS)
882         {
883             r = patch_set_offsets( package->db, patch );
884             if (r == ERROR_SUCCESS)
885                 r = patch_add_media( package, patch_db->storage, patch );
886         }
887     }
888     msi_free( substorage );
889     if (r != ERROR_SUCCESS)
890         return r;
891 
892     r = patch_set_media_source_prop( package );
893     if (r != ERROR_SUCCESS)
894         return r;
895 
896     patch->uninstallable = is_uninstallable( patch_db );
897     patch->state         = MSIPATCHSTATE_APPLIED;
898     list_add_tail( &package->patches, &patch->entry );
899     return ERROR_SUCCESS;
900 }
901 
902 void msi_free_patchinfo( MSIPATCHINFO *patch )
903 {
904     msi_free( patch->patchcode );
905     msi_free( patch->products );
906     msi_free( patch->transforms );
907     msi_free( patch->filename );
908     msi_free( patch->localfile );
909     msi_free( patch );
910 }
911 
912 static UINT msi_apply_patch_package( MSIPACKAGE *package, const WCHAR *file )
913 {
914     static const WCHAR dotmsp[] = {'.','m','s','p',0};
915     MSIDATABASE *patch_db = NULL;
916     WCHAR localfile[MAX_PATH];
917     MSISUMMARYINFO *si;
918     MSIPATCHINFO *patch = NULL;
919     UINT r;
920 
921     TRACE("%p, %s\n", package, debugstr_w(file));
922 
923     r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
924     if (r != ERROR_SUCCESS)
925     {
926         ERR("failed to open patch collection %s\n", debugstr_w( file ) );
927         return r;
928     }
929     r = msi_get_suminfo( patch_db->storage, 0, &si );
930     if (r != ERROR_SUCCESS)
931     {
932         msiobj_release( &patch_db->hdr );
933         return r;
934     }
935     r = msi_check_patch_applicable( package, si );
936     if (r != ERROR_SUCCESS)
937     {
938         TRACE("patch not applicable\n");
939         r = ERROR_SUCCESS;
940         goto done;
941     }
942     r = msi_parse_patch_summary( si, &patch );
943     if ( r != ERROR_SUCCESS )
944         goto done;
945 
946     r = msi_create_empty_local_file( localfile, dotmsp );
947     if ( r != ERROR_SUCCESS )
948         goto done;
949 
950     r = ERROR_OUTOFMEMORY;
951     patch->registered = FALSE;
952     if (!(patch->filename = strdupW( file ))) goto done;
953     if (!(patch->localfile = strdupW( localfile ))) goto done;
954 
955     r = msi_apply_patch_db( package, patch_db, patch );
956     if (r != ERROR_SUCCESS) WARN("patch failed to apply %u\n", r);
957 
958 done:
959     msiobj_release( &si->hdr );
960     msiobj_release( &patch_db->hdr );
961     if (patch && r != ERROR_SUCCESS)
962     {
963         DeleteFileW( patch->localfile );
964         msi_free_patchinfo( patch );
965     }
966     return r;
967 }
968 
969 /* get the PATCH property, and apply all the patches it specifies */
970 UINT msi_apply_patches( MSIPACKAGE *package )
971 {
972     LPWSTR patch_list, *patches;
973     UINT i, r = ERROR_SUCCESS;
974 
975     patch_list = msi_dup_property( package->db, szPatch );
976 
977     TRACE("patches to be applied: %s\n", debugstr_w(patch_list));
978 
979     patches = msi_split_string( patch_list, ';' );
980     for (i = 0; patches && patches[i] && r == ERROR_SUCCESS; i++)
981         r = msi_apply_patch_package( package, patches[i] );
982 
983     msi_free( patches );
984     msi_free( patch_list );
985     return r;
986 }
987 
988 UINT msi_apply_transforms( MSIPACKAGE *package )
989 {
990     static const WCHAR szTransforms[] = {'T','R','A','N','S','F','O','R','M','S',0};
991     LPWSTR xform_list, *xforms;
992     UINT i, r = ERROR_SUCCESS;
993 
994     xform_list = msi_dup_property( package->db, szTransforms );
995     xforms = msi_split_string( xform_list, ';' );
996 
997     for (i = 0; xforms && xforms[i] && r == ERROR_SUCCESS; i++)
998     {
999         if (xforms[i][0] == ':')
1000             r = apply_substorage_transform( package, package->db, xforms[i] );
1001         else
1002         {
1003             WCHAR *transform;
1004 
1005             if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
1006             else
1007             {
1008                 WCHAR *p = strrchrW( package->PackagePath, '\\' );
1009                 DWORD len = p - package->PackagePath + 1;
1010 
1011                 if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
1012                 {
1013                     msi_free( xforms );
1014                     msi_free( xform_list );
1015                     return ERROR_OUTOFMEMORY;
1016                 }
1017                 memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
1018                 memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
1019             }
1020             r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
1021             if (transform != xforms[i]) msi_free( transform );
1022         }
1023     }
1024     msi_free( xforms );
1025     msi_free( xform_list );
1026     return r;
1027 }
1028 
1029 UINT msi_apply_registered_patch( MSIPACKAGE *package, LPCWSTR patch_code )
1030 {
1031     UINT r;
1032     DWORD len;
1033     WCHAR patch_file[MAX_PATH];
1034     MSIDATABASE *patch_db;
1035     MSIPATCHINFO *patch_info;
1036     MSISUMMARYINFO *si;
1037 
1038     TRACE("%p, %s\n", package, debugstr_w(patch_code));
1039 
1040     len = sizeof(patch_file) / sizeof(WCHAR);
1041     r = MsiGetPatchInfoExW( patch_code, package->ProductCode, NULL, package->Context,
1042                             INSTALLPROPERTY_LOCALPACKAGEW, patch_file, &len );
1043     if (r != ERROR_SUCCESS)
1044     {
1045         ERR("failed to get patch filename %u\n", r);
1046         return r;
1047     }
1048     r = MSI_OpenDatabaseW( patch_file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
1049     if (r != ERROR_SUCCESS)
1050     {
1051         ERR("failed to open patch database %s\n", debugstr_w( patch_file ));
1052         return r;
1053     }
1054     r = msi_get_suminfo( patch_db->storage, 0, &si );
1055     if (r != ERROR_SUCCESS)
1056     {
1057         msiobj_release( &patch_db->hdr );
1058         return r;
1059     }
1060     r = msi_parse_patch_summary( si, &patch_info );
1061     msiobj_release( &si->hdr );
1062     if (r != ERROR_SUCCESS)
1063     {
1064         ERR("failed to parse patch summary %u\n", r);
1065         msiobj_release( &patch_db->hdr );
1066         return r;
1067     }
1068     patch_info->registered = TRUE;
1069     patch_info->localfile = strdupW( patch_file );
1070     if (!patch_info->localfile)
1071     {
1072         msiobj_release( &patch_db->hdr );
1073         msi_free_patchinfo( patch_info );
1074         return ERROR_OUTOFMEMORY;
1075     }
1076     r = msi_apply_patch_db( package, patch_db, patch_info );
1077     msiobj_release( &patch_db->hdr );
1078     if (r != ERROR_SUCCESS)
1079     {
1080         ERR("failed to apply patch %u\n", r);
1081         msi_free_patchinfo( patch_info );
1082     }
1083     return r;
1084 }
1085