1 /* 2 * Implementation of the Microsoft Installer (msi.dll) 3 * 4 * Copyright 2002, 2005 Mike McCormack for CodeWeavers 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #include <stdarg.h> 22 23 #define COBJMACROS 24 #define NONAMELESSUNION 25 26 #include "stdio.h" 27 #include "windef.h" 28 #include "winbase.h" 29 #include "winreg.h" 30 #include "winnls.h" 31 #include "shlwapi.h" 32 #include "wine/debug.h" 33 #include "wine/unicode.h" 34 #include "msi.h" 35 #include "msiquery.h" 36 #include "msidefs.h" 37 #include "msipriv.h" 38 #include "objidl.h" 39 #include "propvarutil.h" 40 #include "msiserver.h" 41 42 WINE_DEFAULT_DEBUG_CHANNEL(msi); 43 44 #include "pshpack1.h" 45 46 typedef struct { 47 WORD wByteOrder; 48 WORD wFormat; 49 DWORD dwOSVer; 50 CLSID clsID; 51 DWORD reserved; 52 } PROPERTYSETHEADER; 53 54 typedef struct { 55 FMTID fmtid; 56 DWORD dwOffset; 57 } FORMATIDOFFSET; 58 59 typedef struct { 60 DWORD cbSection; 61 DWORD cProperties; 62 } PROPERTYSECTIONHEADER; 63 64 typedef struct { 65 DWORD propid; 66 DWORD dwOffset; 67 } PROPERTYIDOFFSET; 68 69 typedef struct { 70 DWORD type; 71 union { 72 INT i4; 73 SHORT i2; 74 FILETIME ft; 75 struct { 76 DWORD len; 77 BYTE str[1]; 78 } str; 79 } u; 80 } PROPERTY_DATA; 81 82 #include "poppack.h" 83 84 static HRESULT (WINAPI *pPropVariantChangeType) 85 (PROPVARIANT *ppropvarDest, REFPROPVARIANT propvarSrc, 86 PROPVAR_CHANGE_FLAGS flags, VARTYPE vt); 87 88 #define SECT_HDR_SIZE (sizeof(PROPERTYSECTIONHEADER)) 89 90 static void free_prop( PROPVARIANT *prop ) 91 { 92 if (prop->vt == VT_LPSTR ) 93 msi_free( prop->u.pszVal ); 94 prop->vt = VT_EMPTY; 95 } 96 97 static void MSI_CloseSummaryInfo( MSIOBJECTHDR *arg ) 98 { 99 MSISUMMARYINFO *si = (MSISUMMARYINFO *) arg; 100 DWORD i; 101 102 for( i = 0; i < MSI_MAX_PROPS; i++ ) 103 free_prop( &si->property[i] ); 104 IStorage_Release( si->storage ); 105 } 106 107 static UINT get_type( UINT uiProperty ) 108 { 109 switch( uiProperty ) 110 { 111 case PID_CODEPAGE: 112 return VT_I2; 113 114 case PID_SUBJECT: 115 case PID_AUTHOR: 116 case PID_KEYWORDS: 117 case PID_COMMENTS: 118 case PID_TEMPLATE: 119 case PID_LASTAUTHOR: 120 case PID_REVNUMBER: 121 case PID_APPNAME: 122 case PID_TITLE: 123 return VT_LPSTR; 124 125 case PID_LASTPRINTED: 126 case PID_CREATE_DTM: 127 case PID_LASTSAVE_DTM: 128 return VT_FILETIME; 129 130 case PID_WORDCOUNT: 131 case PID_CHARCOUNT: 132 case PID_SECURITY: 133 case PID_PAGECOUNT: 134 return VT_I4; 135 } 136 return VT_EMPTY; 137 } 138 139 static UINT get_property_count( const PROPVARIANT *property ) 140 { 141 UINT i, n = 0; 142 143 if( !property ) 144 return n; 145 for( i = 0; i < MSI_MAX_PROPS; i++ ) 146 if( property[i].vt != VT_EMPTY ) 147 n++; 148 return n; 149 } 150 151 static UINT propvar_changetype(PROPVARIANT *changed, PROPVARIANT *property, VARTYPE vt) 152 { 153 HRESULT hr; 154 HMODULE propsys = LoadLibraryA("propsys.dll"); 155 pPropVariantChangeType = (void *)GetProcAddress(propsys, "PropVariantChangeType"); 156 157 if (!pPropVariantChangeType) 158 { 159 ERR("PropVariantChangeType function missing!\n"); 160 return ERROR_FUNCTION_FAILED; 161 } 162 163 hr = pPropVariantChangeType(changed, property, 0, vt); 164 return (hr == S_OK) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED; 165 } 166 167 /* FIXME: doesn't deal with endian conversion */ 168 static void read_properties_from_data( PROPVARIANT *prop, LPBYTE data, DWORD sz ) 169 { 170 UINT type; 171 DWORD i, size; 172 PROPERTY_DATA *propdata; 173 PROPVARIANT property, *ptr; 174 PROPVARIANT changed; 175 PROPERTYIDOFFSET *idofs; 176 PROPERTYSECTIONHEADER *section_hdr; 177 178 section_hdr = (PROPERTYSECTIONHEADER*) &data[0]; 179 idofs = (PROPERTYIDOFFSET*) &data[SECT_HDR_SIZE]; 180 181 /* now set all the properties */ 182 for( i = 0; i < section_hdr->cProperties; i++ ) 183 { 184 if( idofs[i].propid >= MSI_MAX_PROPS ) 185 { 186 ERR("Unknown property ID %d\n", idofs[i].propid ); 187 break; 188 } 189 190 type = get_type( idofs[i].propid ); 191 if( type == VT_EMPTY ) 192 { 193 ERR("propid %d has unknown type\n", idofs[i].propid); 194 break; 195 } 196 197 propdata = (PROPERTY_DATA*) &data[ idofs[i].dwOffset ]; 198 199 /* check we don't run off the end of the data */ 200 size = sz - idofs[i].dwOffset - sizeof(DWORD); 201 if( sizeof(DWORD) > size || 202 ( propdata->type == VT_FILETIME && sizeof(FILETIME) > size ) || 203 ( propdata->type == VT_LPSTR && (propdata->u.str.len + sizeof(DWORD)) > size ) ) 204 { 205 ERR("not enough data\n"); 206 break; 207 } 208 209 property.vt = propdata->type; 210 if( propdata->type == VT_LPSTR ) 211 { 212 LPSTR str = msi_alloc( propdata->u.str.len ); 213 memcpy( str, propdata->u.str.str, propdata->u.str.len ); 214 str[ propdata->u.str.len - 1 ] = 0; 215 property.u.pszVal = str; 216 } 217 else if( propdata->type == VT_FILETIME ) 218 property.u.filetime = propdata->u.ft; 219 else if( propdata->type == VT_I2 ) 220 property.u.iVal = propdata->u.i2; 221 else if( propdata->type == VT_I4 ) 222 property.u.lVal = propdata->u.i4; 223 224 /* check the type is the same as we expect */ 225 if( type != propdata->type ) 226 { 227 propvar_changetype(&changed, &property, type); 228 ptr = &changed; 229 } 230 else 231 ptr = &property; 232 233 prop[ idofs[i].propid ] = *ptr; 234 } 235 } 236 237 static UINT load_summary_info( MSISUMMARYINFO *si, IStream *stm ) 238 { 239 PROPERTYSETHEADER set_hdr; 240 FORMATIDOFFSET format_hdr; 241 PROPERTYSECTIONHEADER section_hdr; 242 LPBYTE data = NULL; 243 LARGE_INTEGER ofs; 244 ULONG count, sz; 245 HRESULT r; 246 247 TRACE("%p %p\n", si, stm); 248 249 /* read the header */ 250 sz = sizeof set_hdr; 251 r = IStream_Read( stm, &set_hdr, sz, &count ); 252 if( FAILED(r) || count != sz ) 253 return ERROR_FUNCTION_FAILED; 254 255 if( set_hdr.wByteOrder != 0xfffe ) 256 { 257 ERR("property set not big-endian %04X\n", set_hdr.wByteOrder); 258 return ERROR_FUNCTION_FAILED; 259 } 260 261 sz = sizeof format_hdr; 262 r = IStream_Read( stm, &format_hdr, sz, &count ); 263 if( FAILED(r) || count != sz ) 264 return ERROR_FUNCTION_FAILED; 265 266 /* check the format id is correct */ 267 if( !IsEqualGUID( &FMTID_SummaryInformation, &format_hdr.fmtid ) ) 268 return ERROR_FUNCTION_FAILED; 269 270 /* seek to the location of the section */ 271 ofs.QuadPart = format_hdr.dwOffset; 272 r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, NULL ); 273 if( FAILED(r) ) 274 return ERROR_FUNCTION_FAILED; 275 276 /* read the section itself */ 277 sz = SECT_HDR_SIZE; 278 r = IStream_Read( stm, §ion_hdr, sz, &count ); 279 if( FAILED(r) || count != sz ) 280 return ERROR_FUNCTION_FAILED; 281 282 if( section_hdr.cProperties > MSI_MAX_PROPS ) 283 { 284 ERR("too many properties %d\n", section_hdr.cProperties); 285 return ERROR_FUNCTION_FAILED; 286 } 287 288 data = msi_alloc( section_hdr.cbSection); 289 if( !data ) 290 return ERROR_FUNCTION_FAILED; 291 292 memcpy( data, §ion_hdr, SECT_HDR_SIZE ); 293 294 /* read all the data in one go */ 295 sz = section_hdr.cbSection - SECT_HDR_SIZE; 296 r = IStream_Read( stm, &data[ SECT_HDR_SIZE ], sz, &count ); 297 if( SUCCEEDED(r) && count == sz ) 298 read_properties_from_data( si->property, data, sz + SECT_HDR_SIZE ); 299 else 300 ERR("failed to read properties %d %d\n", count, sz); 301 302 msi_free( data ); 303 return ERROR_SUCCESS; 304 } 305 306 static DWORD write_dword( LPBYTE data, DWORD ofs, DWORD val ) 307 { 308 if( data ) 309 { 310 data[ofs++] = val&0xff; 311 data[ofs++] = (val>>8)&0xff; 312 data[ofs++] = (val>>16)&0xff; 313 data[ofs++] = (val>>24)&0xff; 314 } 315 return 4; 316 } 317 318 static DWORD write_filetime( LPBYTE data, DWORD ofs, const FILETIME *ft ) 319 { 320 write_dword( data, ofs, ft->dwLowDateTime ); 321 write_dword( data, ofs + 4, ft->dwHighDateTime ); 322 return 8; 323 } 324 325 static DWORD write_string( LPBYTE data, DWORD ofs, LPCSTR str ) 326 { 327 DWORD len = lstrlenA( str ) + 1; 328 write_dword( data, ofs, len ); 329 if( data ) 330 memcpy( &data[ofs + 4], str, len ); 331 return (7 + len) & ~3; 332 } 333 334 static UINT write_property_to_data( const PROPVARIANT *prop, LPBYTE data ) 335 { 336 DWORD sz = 0; 337 338 if( prop->vt == VT_EMPTY ) 339 return sz; 340 341 /* add the type */ 342 sz += write_dword( data, sz, prop->vt ); 343 switch( prop->vt ) 344 { 345 case VT_I2: 346 sz += write_dword( data, sz, prop->u.iVal ); 347 break; 348 case VT_I4: 349 sz += write_dword( data, sz, prop->u.lVal ); 350 break; 351 case VT_FILETIME: 352 sz += write_filetime( data, sz, &prop->u.filetime ); 353 break; 354 case VT_LPSTR: 355 sz += write_string( data, sz, prop->u.pszVal ); 356 break; 357 } 358 return sz; 359 } 360 361 static UINT save_summary_info( const MSISUMMARYINFO * si, IStream *stm ) 362 { 363 UINT ret = ERROR_FUNCTION_FAILED; 364 PROPERTYSETHEADER set_hdr; 365 FORMATIDOFFSET format_hdr; 366 PROPERTYSECTIONHEADER section_hdr; 367 PROPERTYIDOFFSET idofs[MSI_MAX_PROPS]; 368 LPBYTE data = NULL; 369 ULONG count, sz; 370 HRESULT r; 371 int i; 372 373 /* write the header */ 374 sz = sizeof set_hdr; 375 memset( &set_hdr, 0, sz ); 376 set_hdr.wByteOrder = 0xfffe; 377 set_hdr.wFormat = 0; 378 set_hdr.dwOSVer = 0x00020005; /* build 5, platform id 2 */ 379 /* set_hdr.clsID is {00000000-0000-0000-0000-000000000000} */ 380 set_hdr.reserved = 1; 381 r = IStream_Write( stm, &set_hdr, sz, &count ); 382 if( FAILED(r) || count != sz ) 383 return ret; 384 385 /* write the format header */ 386 sz = sizeof format_hdr; 387 format_hdr.fmtid = FMTID_SummaryInformation; 388 format_hdr.dwOffset = sizeof format_hdr + sizeof set_hdr; 389 r = IStream_Write( stm, &format_hdr, sz, &count ); 390 if( FAILED(r) || count != sz ) 391 return ret; 392 393 /* add up how much space the data will take and calculate the offsets */ 394 section_hdr.cbSection = sizeof section_hdr; 395 section_hdr.cbSection += (get_property_count( si->property ) * sizeof idofs[0]); 396 section_hdr.cProperties = 0; 397 for( i = 0; i < MSI_MAX_PROPS; i++ ) 398 { 399 sz = write_property_to_data( &si->property[i], NULL ); 400 if( !sz ) 401 continue; 402 idofs[ section_hdr.cProperties ].propid = i; 403 idofs[ section_hdr.cProperties ].dwOffset = section_hdr.cbSection; 404 section_hdr.cProperties++; 405 section_hdr.cbSection += sz; 406 } 407 408 data = msi_alloc_zero( section_hdr.cbSection ); 409 410 sz = 0; 411 memcpy( &data[sz], §ion_hdr, sizeof section_hdr ); 412 sz += sizeof section_hdr; 413 414 memcpy( &data[sz], idofs, section_hdr.cProperties * sizeof idofs[0] ); 415 sz += section_hdr.cProperties * sizeof idofs[0]; 416 417 /* write out the data */ 418 for( i = 0; i < MSI_MAX_PROPS; i++ ) 419 sz += write_property_to_data( &si->property[i], &data[sz] ); 420 421 r = IStream_Write( stm, data, sz, &count ); 422 msi_free( data ); 423 if( FAILED(r) || count != sz ) 424 return ret; 425 426 return ERROR_SUCCESS; 427 } 428 429 static MSISUMMARYINFO *create_suminfo( IStorage *stg, UINT update_count ) 430 { 431 MSISUMMARYINFO *si; 432 433 if (!(si = alloc_msiobject( MSIHANDLETYPE_SUMMARYINFO, sizeof(MSISUMMARYINFO), MSI_CloseSummaryInfo ))) 434 return NULL; 435 436 si->update_count = update_count; 437 IStorage_AddRef( stg ); 438 si->storage = stg; 439 440 return si; 441 } 442 443 UINT msi_get_suminfo( IStorage *stg, UINT uiUpdateCount, MSISUMMARYINFO **ret ) 444 { 445 IStream *stm; 446 MSISUMMARYINFO *si; 447 HRESULT hr; 448 UINT r; 449 450 TRACE("%p, %u\n", stg, uiUpdateCount); 451 452 if (!(si = create_suminfo( stg, uiUpdateCount ))) return ERROR_OUTOFMEMORY; 453 454 hr = IStorage_OpenStream( si->storage, szSumInfo, 0, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, &stm ); 455 if (FAILED( hr )) 456 { 457 msiobj_release( &si->hdr ); 458 return ERROR_FUNCTION_FAILED; 459 } 460 461 r = load_summary_info( si, stm ); 462 IStream_Release( stm ); 463 if (r != ERROR_SUCCESS) 464 { 465 msiobj_release( &si->hdr ); 466 return r; 467 } 468 469 *ret = si; 470 return ERROR_SUCCESS; 471 } 472 473 UINT msi_get_db_suminfo( MSIDATABASE *db, UINT uiUpdateCount, MSISUMMARYINFO **ret ) 474 { 475 IStream *stm; 476 MSISUMMARYINFO *si; 477 UINT r; 478 479 if (!(si = create_suminfo( db->storage, uiUpdateCount ))) return ERROR_OUTOFMEMORY; 480 481 r = msi_get_stream( db, szSumInfo, &stm ); 482 if (r != ERROR_SUCCESS) 483 { 484 msiobj_release( &si->hdr ); 485 return r; 486 } 487 488 r = load_summary_info( si, stm ); 489 IStream_Release( stm ); 490 if (r != ERROR_SUCCESS) 491 { 492 msiobj_release( &si->hdr ); 493 return r; 494 } 495 496 *ret = si; 497 return ERROR_SUCCESS; 498 } 499 500 UINT WINAPI MsiGetSummaryInformationW( MSIHANDLE hDatabase, 501 LPCWSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle ) 502 { 503 MSISUMMARYINFO *si; 504 MSIDATABASE *db; 505 UINT ret; 506 507 TRACE("%d %s %d %p\n", hDatabase, debugstr_w(szDatabase), 508 uiUpdateCount, pHandle); 509 510 if( !pHandle ) 511 return ERROR_INVALID_PARAMETER; 512 513 if( szDatabase && szDatabase[0] ) 514 { 515 LPCWSTR persist = uiUpdateCount ? MSIDBOPEN_TRANSACT : MSIDBOPEN_READONLY; 516 517 ret = MSI_OpenDatabaseW( szDatabase, persist, &db ); 518 if( ret != ERROR_SUCCESS ) 519 return ret; 520 } 521 else 522 { 523 db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE ); 524 if( !db ) 525 { 526 HRESULT hr; 527 IWineMsiRemoteDatabase *remote_database; 528 529 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hDatabase ); 530 if ( !remote_database ) 531 return ERROR_INVALID_HANDLE; 532 533 hr = IWineMsiRemoteDatabase_GetSummaryInformation( remote_database, 534 uiUpdateCount, pHandle ); 535 IWineMsiRemoteDatabase_Release( remote_database ); 536 537 if (FAILED(hr)) 538 { 539 if (HRESULT_FACILITY(hr) == FACILITY_WIN32) 540 return HRESULT_CODE(hr); 541 542 return ERROR_FUNCTION_FAILED; 543 } 544 545 return ERROR_SUCCESS; 546 } 547 } 548 549 ret = msi_get_suminfo( db->storage, uiUpdateCount, &si ); 550 if (ret != ERROR_SUCCESS) 551 ret = msi_get_db_suminfo( db, uiUpdateCount, &si ); 552 if (ret != ERROR_SUCCESS) 553 { 554 if ((si = create_suminfo( db->storage, uiUpdateCount ))) 555 ret = ERROR_SUCCESS; 556 } 557 558 if (ret == ERROR_SUCCESS) 559 { 560 *pHandle = alloc_msihandle( &si->hdr ); 561 if( *pHandle ) 562 ret = ERROR_SUCCESS; 563 else 564 ret = ERROR_NOT_ENOUGH_MEMORY; 565 msiobj_release( &si->hdr ); 566 } 567 568 msiobj_release( &db->hdr ); 569 return ret; 570 } 571 572 UINT WINAPI MsiGetSummaryInformationA(MSIHANDLE hDatabase, 573 LPCSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle) 574 { 575 LPWSTR szwDatabase = NULL; 576 UINT ret; 577 578 TRACE("%d %s %d %p\n", hDatabase, debugstr_a(szDatabase), 579 uiUpdateCount, pHandle); 580 581 if( szDatabase ) 582 { 583 szwDatabase = strdupAtoW( szDatabase ); 584 if( !szwDatabase ) 585 return ERROR_FUNCTION_FAILED; 586 } 587 588 ret = MsiGetSummaryInformationW(hDatabase, szwDatabase, uiUpdateCount, pHandle); 589 590 msi_free( szwDatabase ); 591 592 return ret; 593 } 594 595 UINT WINAPI MsiSummaryInfoGetPropertyCount(MSIHANDLE hSummaryInfo, PUINT pCount) 596 { 597 MSISUMMARYINFO *si; 598 599 TRACE("%d %p\n", hSummaryInfo, pCount); 600 601 si = msihandle2msiinfo( hSummaryInfo, MSIHANDLETYPE_SUMMARYINFO ); 602 if( !si ) 603 return ERROR_INVALID_HANDLE; 604 605 if( pCount ) 606 *pCount = get_property_count( si->property ); 607 msiobj_release( &si->hdr ); 608 609 return ERROR_SUCCESS; 610 } 611 612 static UINT get_prop( MSISUMMARYINFO *si, UINT uiProperty, UINT *puiDataType, INT *piValue, 613 FILETIME *pftValue, awstring *str, DWORD *pcchValueBuf) 614 { 615 PROPVARIANT *prop; 616 UINT ret = ERROR_SUCCESS; 617 618 prop = &si->property[uiProperty]; 619 620 if( puiDataType ) 621 *puiDataType = prop->vt; 622 623 switch( prop->vt ) 624 { 625 case VT_I2: 626 if( piValue ) 627 *piValue = prop->u.iVal; 628 break; 629 case VT_I4: 630 if( piValue ) 631 *piValue = prop->u.lVal; 632 break; 633 case VT_LPSTR: 634 if( pcchValueBuf ) 635 { 636 DWORD len = 0; 637 638 if( str->unicode ) 639 { 640 len = MultiByteToWideChar( CP_ACP, 0, prop->u.pszVal, -1, NULL, 0 ) - 1; 641 MultiByteToWideChar( CP_ACP, 0, prop->u.pszVal, -1, str->str.w, *pcchValueBuf ); 642 } 643 else 644 { 645 len = lstrlenA( prop->u.pszVal ); 646 if( str->str.a ) 647 lstrcpynA(str->str.a, prop->u.pszVal, *pcchValueBuf ); 648 } 649 if (len >= *pcchValueBuf) 650 ret = ERROR_MORE_DATA; 651 *pcchValueBuf = len; 652 } 653 break; 654 case VT_FILETIME: 655 if( pftValue ) 656 *pftValue = prop->u.filetime; 657 break; 658 case VT_EMPTY: 659 break; 660 default: 661 FIXME("Unknown property variant type\n"); 662 break; 663 } 664 return ret; 665 } 666 667 LPWSTR msi_suminfo_dup_string( MSISUMMARYINFO *si, UINT uiProperty ) 668 { 669 PROPVARIANT *prop; 670 671 if ( uiProperty >= MSI_MAX_PROPS ) 672 return NULL; 673 prop = &si->property[uiProperty]; 674 if( prop->vt != VT_LPSTR ) 675 return NULL; 676 return strdupAtoW( prop->u.pszVal ); 677 } 678 679 INT msi_suminfo_get_int32( MSISUMMARYINFO *si, UINT uiProperty ) 680 { 681 PROPVARIANT *prop; 682 683 if ( uiProperty >= MSI_MAX_PROPS ) 684 return -1; 685 prop = &si->property[uiProperty]; 686 if( prop->vt != VT_I4 ) 687 return -1; 688 return prop->u.lVal; 689 } 690 691 LPWSTR msi_get_suminfo_product( IStorage *stg ) 692 { 693 MSISUMMARYINFO *si; 694 LPWSTR prod; 695 UINT r; 696 697 r = msi_get_suminfo( stg, 0, &si ); 698 if (r != ERROR_SUCCESS) 699 { 700 ERR("no summary information!\n"); 701 return NULL; 702 } 703 prod = msi_suminfo_dup_string( si, PID_REVNUMBER ); 704 msiobj_release( &si->hdr ); 705 return prod; 706 } 707 708 UINT WINAPI MsiSummaryInfoGetPropertyA( 709 MSIHANDLE handle, UINT uiProperty, PUINT puiDataType, LPINT piValue, 710 FILETIME *pftValue, LPSTR szValueBuf, LPDWORD pcchValueBuf) 711 { 712 MSISUMMARYINFO *si; 713 awstring str; 714 UINT r; 715 716 TRACE("%u, %u, %p, %p, %p, %p, %p\n", handle, uiProperty, puiDataType, 717 piValue, pftValue, szValueBuf, pcchValueBuf ); 718 719 if (uiProperty >= MSI_MAX_PROPS) 720 { 721 if (puiDataType) *puiDataType = VT_EMPTY; 722 return ERROR_UNKNOWN_PROPERTY; 723 } 724 725 if (!(si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO ))) 726 return ERROR_INVALID_HANDLE; 727 728 str.unicode = FALSE; 729 str.str.a = szValueBuf; 730 731 r = get_prop( si, uiProperty, puiDataType, piValue, pftValue, &str, pcchValueBuf ); 732 msiobj_release( &si->hdr ); 733 return r; 734 } 735 736 UINT WINAPI MsiSummaryInfoGetPropertyW( 737 MSIHANDLE handle, UINT uiProperty, PUINT puiDataType, LPINT piValue, 738 FILETIME *pftValue, LPWSTR szValueBuf, LPDWORD pcchValueBuf) 739 { 740 MSISUMMARYINFO *si; 741 awstring str; 742 UINT r; 743 744 TRACE("%u, %u, %p, %p, %p, %p, %p\n", handle, uiProperty, puiDataType, 745 piValue, pftValue, szValueBuf, pcchValueBuf ); 746 747 if (uiProperty >= MSI_MAX_PROPS) 748 { 749 if (puiDataType) *puiDataType = VT_EMPTY; 750 return ERROR_UNKNOWN_PROPERTY; 751 } 752 753 if (!(si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO ))) 754 return ERROR_INVALID_HANDLE; 755 756 str.unicode = TRUE; 757 str.str.w = szValueBuf; 758 759 r = get_prop( si, uiProperty, puiDataType, piValue, pftValue, &str, pcchValueBuf ); 760 msiobj_release( &si->hdr ); 761 return r; 762 } 763 764 static UINT set_prop( MSISUMMARYINFO *si, UINT uiProperty, UINT type, 765 INT iValue, FILETIME *pftValue, awcstring *str ) 766 { 767 PROPVARIANT *prop; 768 UINT len; 769 770 TRACE("%p, %u, %u, %d, %p, %p\n", si, uiProperty, type, iValue, pftValue, str ); 771 772 prop = &si->property[uiProperty]; 773 774 if( prop->vt == VT_EMPTY ) 775 { 776 if( !si->update_count ) 777 return ERROR_FUNCTION_FAILED; 778 779 si->update_count--; 780 } 781 else if( prop->vt != type ) 782 return ERROR_SUCCESS; 783 784 free_prop( prop ); 785 prop->vt = type; 786 switch( type ) 787 { 788 case VT_I4: 789 prop->u.lVal = iValue; 790 break; 791 case VT_I2: 792 prop->u.iVal = iValue; 793 break; 794 case VT_FILETIME: 795 prop->u.filetime = *pftValue; 796 break; 797 case VT_LPSTR: 798 if( str->unicode ) 799 { 800 len = WideCharToMultiByte( CP_ACP, 0, str->str.w, -1, 801 NULL, 0, NULL, NULL ); 802 prop->u.pszVal = msi_alloc( len ); 803 WideCharToMultiByte( CP_ACP, 0, str->str.w, -1, 804 prop->u.pszVal, len, NULL, NULL ); 805 } 806 else 807 { 808 len = lstrlenA( str->str.a ) + 1; 809 prop->u.pszVal = msi_alloc( len ); 810 lstrcpyA( prop->u.pszVal, str->str.a ); 811 } 812 break; 813 } 814 815 return ERROR_SUCCESS; 816 } 817 818 UINT WINAPI MsiSummaryInfoSetPropertyW( MSIHANDLE handle, UINT uiProperty, UINT uiDataType, 819 INT iValue, FILETIME *pftValue, LPCWSTR szValue ) 820 { 821 awcstring str; 822 MSISUMMARYINFO *si; 823 UINT type, ret; 824 825 TRACE("%u, %u, %u, %d, %p, %s\n", handle, uiProperty, uiDataType, iValue, pftValue, 826 debugstr_w(szValue) ); 827 828 type = get_type( uiProperty ); 829 if( type == VT_EMPTY || type != uiDataType ) 830 return ERROR_DATATYPE_MISMATCH; 831 832 if( uiDataType == VT_LPSTR && !szValue ) 833 return ERROR_INVALID_PARAMETER; 834 835 if( uiDataType == VT_FILETIME && !pftValue ) 836 return ERROR_INVALID_PARAMETER; 837 838 if (!(si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO ))) 839 return ERROR_INVALID_HANDLE; 840 841 str.unicode = TRUE; 842 str.str.w = szValue; 843 844 ret = set_prop( si, uiProperty, type, iValue, pftValue, &str ); 845 msiobj_release( &si->hdr ); 846 return ret; 847 } 848 849 UINT WINAPI MsiSummaryInfoSetPropertyA( MSIHANDLE handle, UINT uiProperty, UINT uiDataType, 850 INT iValue, FILETIME *pftValue, LPCSTR szValue ) 851 { 852 awcstring str; 853 MSISUMMARYINFO *si; 854 UINT type, ret; 855 856 TRACE("%u, %u, %u, %d, %p, %s\n", handle, uiProperty, uiDataType, iValue, pftValue, 857 debugstr_a(szValue) ); 858 859 type = get_type( uiProperty ); 860 if( type == VT_EMPTY || type != uiDataType ) 861 return ERROR_DATATYPE_MISMATCH; 862 863 if( uiDataType == VT_LPSTR && !szValue ) 864 return ERROR_INVALID_PARAMETER; 865 866 if( uiDataType == VT_FILETIME && !pftValue ) 867 return ERROR_INVALID_PARAMETER; 868 869 if (!(si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO ))) 870 return ERROR_INVALID_HANDLE; 871 872 str.unicode = FALSE; 873 str.str.a = szValue; 874 875 ret = set_prop( si, uiProperty, uiDataType, iValue, pftValue, &str ); 876 msiobj_release( &si->hdr ); 877 return ret; 878 } 879 880 static UINT suminfo_persist( MSISUMMARYINFO *si ) 881 { 882 UINT ret = ERROR_FUNCTION_FAILED; 883 IStream *stm = NULL; 884 DWORD grfMode; 885 HRESULT r; 886 887 grfMode = STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE; 888 r = IStorage_CreateStream( si->storage, szSumInfo, grfMode, 0, 0, &stm ); 889 if( SUCCEEDED(r) ) 890 { 891 ret = save_summary_info( si, stm ); 892 IStream_Release( stm ); 893 } 894 return ret; 895 } 896 897 static void parse_filetime( LPCWSTR str, FILETIME *ft ) 898 { 899 SYSTEMTIME lt, utc; 900 const WCHAR *p = str; 901 WCHAR *end; 902 903 memset( <, 0, sizeof(lt) ); 904 905 /* YYYY/MM/DD hh:mm:ss */ 906 907 while (isspaceW( *p )) p++; 908 909 lt.wYear = strtolW( p, &end, 10 ); 910 if (*end != '/') return; 911 p = end + 1; 912 913 lt.wMonth = strtolW( p, &end, 10 ); 914 if (*end != '/') return; 915 p = end + 1; 916 917 lt.wDay = strtolW( p, &end, 10 ); 918 if (*end != ' ') return; 919 p = end + 1; 920 921 while (isspaceW( *p )) p++; 922 923 lt.wHour = strtolW( p, &end, 10 ); 924 if (*end != ':') return; 925 p = end + 1; 926 927 lt.wMinute = strtolW( p, &end, 10 ); 928 if (*end != ':') return; 929 p = end + 1; 930 931 lt.wSecond = strtolW( p, &end, 10 ); 932 933 TzSpecificLocalTimeToSystemTime( NULL, <, &utc ); 934 SystemTimeToFileTime( &utc, ft ); 935 } 936 937 static UINT parse_prop( LPCWSTR prop, LPCWSTR value, UINT *pid, INT *int_value, 938 FILETIME *ft_value, awcstring *str_value ) 939 { 940 *pid = atoiW( prop ); 941 switch (*pid) 942 { 943 case PID_CODEPAGE: 944 case PID_WORDCOUNT: 945 case PID_CHARCOUNT: 946 case PID_SECURITY: 947 case PID_PAGECOUNT: 948 *int_value = atoiW( value ); 949 break; 950 951 case PID_LASTPRINTED: 952 case PID_CREATE_DTM: 953 case PID_LASTSAVE_DTM: 954 parse_filetime( value, ft_value ); 955 break; 956 957 case PID_SUBJECT: 958 case PID_AUTHOR: 959 case PID_KEYWORDS: 960 case PID_COMMENTS: 961 case PID_TEMPLATE: 962 case PID_LASTAUTHOR: 963 case PID_REVNUMBER: 964 case PID_APPNAME: 965 case PID_TITLE: 966 str_value->str.w = value; 967 str_value->unicode = TRUE; 968 break; 969 970 default: 971 WARN("unhandled prop id %u\n", *pid); 972 return ERROR_FUNCTION_FAILED; 973 } 974 975 return ERROR_SUCCESS; 976 } 977 978 UINT msi_add_suminfo( MSIDATABASE *db, LPWSTR **records, int num_records, int num_columns ) 979 { 980 UINT r; 981 int i, j; 982 MSISUMMARYINFO *si; 983 984 r = msi_get_suminfo( db->storage, num_records * (num_columns / 2), &si ); 985 if (r != ERROR_SUCCESS) 986 { 987 if (!(si = create_suminfo( db->storage, num_records * (num_columns / 2) ))) 988 return ERROR_OUTOFMEMORY; 989 r = ERROR_SUCCESS; 990 } 991 992 for (i = 0; i < num_records; i++) 993 { 994 for (j = 0; j < num_columns; j += 2) 995 { 996 UINT pid; 997 INT int_value = 0; 998 FILETIME ft_value; 999 awcstring str_value; 1000 1001 r = parse_prop( records[i][j], records[i][j + 1], &pid, &int_value, &ft_value, &str_value ); 1002 if (r != ERROR_SUCCESS) 1003 goto end; 1004 1005 r = set_prop( si, pid, get_type(pid), int_value, &ft_value, &str_value ); 1006 if (r != ERROR_SUCCESS) 1007 goto end; 1008 } 1009 } 1010 1011 end: 1012 if (r == ERROR_SUCCESS) 1013 r = suminfo_persist( si ); 1014 1015 msiobj_release( &si->hdr ); 1016 return r; 1017 } 1018 1019 static UINT save_prop( MSISUMMARYINFO *si, HANDLE handle, UINT row ) 1020 { 1021 static const char fmt_systemtime[] = "%d/%02d/%02d %02d:%02d:%02d"; 1022 char data[20]; /* largest string: YYYY/MM/DD hh:mm:ss */ 1023 static const char fmt_begin[] = "%u\t"; 1024 static const char data_end[] = "\r\n"; 1025 static const char fmt_int[] = "%u"; 1026 UINT r, data_type, len; 1027 SYSTEMTIME system_time; 1028 FILETIME file_time; 1029 INT int_value; 1030 awstring str; 1031 DWORD sz; 1032 1033 str.unicode = FALSE; 1034 str.str.a = NULL; 1035 len = 0; 1036 r = get_prop( si, row, &data_type, &int_value, &file_time, &str, &len ); 1037 if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA) 1038 return r; 1039 if (data_type == VT_EMPTY) 1040 return ERROR_SUCCESS; /* property not set */ 1041 snprintf( data, sizeof(data), fmt_begin, row ); 1042 sz = lstrlenA( data ); 1043 if (!WriteFile( handle, data, sz, &sz, NULL )) 1044 return ERROR_WRITE_FAULT; 1045 1046 switch (data_type) 1047 { 1048 case VT_I2: 1049 case VT_I4: 1050 snprintf( data, sizeof(data), fmt_int, int_value ); 1051 sz = lstrlenA( data ); 1052 if (!WriteFile( handle, data, sz, &sz, NULL )) 1053 return ERROR_WRITE_FAULT; 1054 break; 1055 case VT_LPSTR: 1056 len++; 1057 if (!(str.str.a = msi_alloc( len ))) 1058 return ERROR_OUTOFMEMORY; 1059 r = get_prop( si, row, NULL, NULL, NULL, &str, &len ); 1060 if (r != ERROR_SUCCESS) 1061 { 1062 msi_free( str.str.a ); 1063 return r; 1064 } 1065 sz = lstrlenA( str.str.a ); 1066 if (!WriteFile( handle, str.str.a, sz, &sz, NULL )) 1067 { 1068 msi_free( str.str.a ); 1069 return ERROR_WRITE_FAULT; 1070 } 1071 msi_free( str.str.a ); 1072 break; 1073 case VT_FILETIME: 1074 if (!FileTimeToSystemTime( &file_time, &system_time )) 1075 return ERROR_FUNCTION_FAILED; 1076 snprintf( data, sizeof(data), fmt_systemtime, system_time.wYear, system_time.wMonth, 1077 system_time.wDay, system_time.wHour, system_time.wMinute, 1078 system_time.wSecond ); 1079 sz = lstrlenA( data ); 1080 if (!WriteFile( handle, data, sz, &sz, NULL )) 1081 return ERROR_WRITE_FAULT; 1082 break; 1083 case VT_EMPTY: 1084 /* cannot reach here, property not set */ 1085 break; 1086 default: 1087 FIXME( "Unknown property variant type\n" ); 1088 return ERROR_FUNCTION_FAILED; 1089 } 1090 1091 sz = lstrlenA( data_end ); 1092 if (!WriteFile( handle, data_end, sz, &sz, NULL )) 1093 return ERROR_WRITE_FAULT; 1094 1095 return ERROR_SUCCESS; 1096 } 1097 1098 UINT msi_export_suminfo( MSIDATABASE *db, HANDLE handle ) 1099 { 1100 UINT i, r, num_rows; 1101 MSISUMMARYINFO *si; 1102 1103 r = msi_get_suminfo( db->storage, 0, &si ); 1104 if (r != ERROR_SUCCESS) 1105 r = msi_get_db_suminfo( db, 0, &si ); 1106 if (r != ERROR_SUCCESS) 1107 return r; 1108 1109 num_rows = get_property_count( si->property ); 1110 if (!num_rows) 1111 { 1112 msiobj_release( &si->hdr ); 1113 return ERROR_FUNCTION_FAILED; 1114 } 1115 1116 for (i = 0; i < num_rows; i++) 1117 { 1118 r = save_prop( si, handle, i ); 1119 if (r != ERROR_SUCCESS) 1120 { 1121 msiobj_release( &si->hdr ); 1122 return r; 1123 } 1124 } 1125 1126 msiobj_release( &si->hdr ); 1127 return ERROR_SUCCESS; 1128 } 1129 1130 UINT WINAPI MsiSummaryInfoPersist( MSIHANDLE handle ) 1131 { 1132 MSISUMMARYINFO *si; 1133 UINT ret; 1134 1135 TRACE("%d\n", handle ); 1136 1137 si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO ); 1138 if( !si ) 1139 return ERROR_INVALID_HANDLE; 1140 1141 ret = suminfo_persist( si ); 1142 1143 msiobj_release( &si->hdr ); 1144 return ret; 1145 } 1146 1147 UINT WINAPI MsiCreateTransformSummaryInfoA( MSIHANDLE db, MSIHANDLE db_ref, LPCSTR transform, int error, int validation ) 1148 { 1149 UINT r; 1150 WCHAR *transformW = NULL; 1151 1152 TRACE("%u, %u, %s, %d, %d\n", db, db_ref, debugstr_a(transform), error, validation); 1153 1154 if (transform && !(transformW = strdupAtoW( transform ))) 1155 return ERROR_OUTOFMEMORY; 1156 1157 r = MsiCreateTransformSummaryInfoW( db, db_ref, transformW, error, validation ); 1158 msi_free( transformW ); 1159 return r; 1160 } 1161 1162 UINT WINAPI MsiCreateTransformSummaryInfoW( MSIHANDLE db, MSIHANDLE db_ref, LPCWSTR transform, int error, int validation ) 1163 { 1164 FIXME("%u, %u, %s, %d, %d\n", db, db_ref, debugstr_w(transform), error, validation); 1165 return ERROR_FUNCTION_FAILED; 1166 } 1167 1168 UINT msi_load_suminfo_properties( MSIPACKAGE *package ) 1169 { 1170 static const WCHAR packagecodeW[] = {'P','a','c','k','a','g','e','C','o','d','e',0}; 1171 MSISUMMARYINFO *si; 1172 WCHAR *package_code; 1173 UINT r, len; 1174 awstring str; 1175 INT count; 1176 1177 r = msi_get_suminfo( package->db->storage, 0, &si ); 1178 if (r != ERROR_SUCCESS) 1179 { 1180 r = msi_get_db_suminfo( package->db, 0, &si ); 1181 if (r != ERROR_SUCCESS) 1182 { 1183 ERR("Unable to open summary information stream %u\n", r); 1184 return r; 1185 } 1186 } 1187 1188 str.unicode = TRUE; 1189 str.str.w = NULL; 1190 len = 0; 1191 r = get_prop( si, PID_REVNUMBER, NULL, NULL, NULL, &str, &len ); 1192 if (r != ERROR_MORE_DATA) 1193 { 1194 WARN("Unable to query revision number %u\n", r); 1195 msiobj_release( &si->hdr ); 1196 return ERROR_FUNCTION_FAILED; 1197 } 1198 1199 len++; 1200 if (!(package_code = msi_alloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY; 1201 str.str.w = package_code; 1202 1203 r = get_prop( si, PID_REVNUMBER, NULL, NULL, NULL, &str, &len ); 1204 if (r != ERROR_SUCCESS) 1205 { 1206 msi_free( package_code ); 1207 msiobj_release( &si->hdr ); 1208 return r; 1209 } 1210 1211 r = msi_set_property( package->db, packagecodeW, package_code, len ); 1212 msi_free( package_code ); 1213 1214 count = 0; 1215 get_prop( si, PID_WORDCOUNT, NULL, &count, NULL, NULL, NULL ); 1216 package->WordCount = count; 1217 1218 msiobj_release( &si->hdr ); 1219 return r; 1220 } 1221