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