[MSI] Prevent uninitialized variable usage
[reactos.git] / 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 <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 }