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