suminfo.c 25.7 KB
Newer Older
1 2 3
/*
 * Implementation of the Microsoft Installer (msi.dll)
 *
4
 * Copyright 2002, 2005 Mike McCormack for CodeWeavers
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 22
#include <stdarg.h>

23 24 25
#define COBJMACROS
#define NONAMELESSUNION

26 27 28
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
29
#include "winnls.h"
30 31
#include "shlwapi.h"
#include "wine/debug.h"
32
#include "wine/unicode.h"
33 34
#include "msi.h"
#include "msiquery.h"
35
#include "msidefs.h"
36 37
#include "msipriv.h"
#include "objidl.h"
38
#include "propvarutil.h"
39
#include "msiserver.h"
40 41 42

WINE_DEFAULT_DEBUG_CHANNEL(msi);

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
#include "pshpack1.h"

typedef struct { 
    WORD wByteOrder;
    WORD wFormat;
    DWORD dwOSVer;
    CLSID clsID;
    DWORD reserved;
} PROPERTYSETHEADER;

typedef struct { 
    FMTID fmtid;
    DWORD dwOffset;
} FORMATIDOFFSET;

typedef struct { 
    DWORD cbSection;
    DWORD cProperties;
} PROPERTYSECTIONHEADER; 
 
typedef struct { 
    DWORD propid;
    DWORD dwOffset;
} PROPERTYIDOFFSET; 

typedef struct {
    DWORD type;
    union {
        INT i4;
        SHORT i2;
        FILETIME ft;
        struct {
            DWORD len;
            BYTE str[1];
        } str;
    } u;
} PROPERTY_DATA;
 
#include "poppack.h"

83 84 85 86
static HRESULT (WINAPI *pPropVariantChangeType)
    (PROPVARIANT *ppropvarDest, REFPROPVARIANT propvarSrc,
     PROPVAR_CHANGE_FLAGS flags, VARTYPE vt);

87 88
#define SECT_HDR_SIZE (sizeof(PROPERTYSECTIONHEADER))

89 90 91
static void free_prop( PROPVARIANT *prop )
{
    if (prop->vt == VT_LPSTR )
92
        msi_free( prop->u.pszVal );
93 94 95
    prop->vt = VT_EMPTY;
}

96
static void MSI_CloseSummaryInfo( MSIOBJECTHDR *arg )
97
{
98
    MSISUMMARYINFO *si = (MSISUMMARYINFO *) arg;
99 100 101 102
    DWORD i;

    for( i = 0; i < MSI_MAX_PROPS; i++ )
        free_prop( &si->property[i] );
103
    IStorage_Release( si->storage );
104 105
}

106
static UINT get_type( UINT uiProperty )
107
{
108 109 110 111
    switch( uiProperty )
    {
    case PID_CODEPAGE:
         return VT_I2;
112

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
    case PID_SUBJECT:
    case PID_AUTHOR:
    case PID_KEYWORDS:
    case PID_COMMENTS:
    case PID_TEMPLATE:
    case PID_LASTAUTHOR:
    case PID_REVNUMBER:
    case PID_APPNAME:
    case PID_TITLE:
         return VT_LPSTR;

    case PID_LASTPRINTED:
    case PID_CREATE_DTM:
    case PID_LASTSAVE_DTM:
         return VT_FILETIME;

    case PID_WORDCOUNT:
    case PID_CHARCOUNT:
    case PID_SECURITY:
    case PID_PAGECOUNT:
         return VT_I4;
    }
    return VT_EMPTY;
}
137

138
static UINT get_property_count( const PROPVARIANT *property )
139 140 141 142 143
{
    UINT i, n = 0;

    if( !property )
        return n;
144
    for( i = 0; i < MSI_MAX_PROPS; i++ )
145 146 147 148
        if( property[i].vt != VT_EMPTY )
            n++;
    return n;
}
149

150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
static UINT propvar_changetype(PROPVARIANT *changed, PROPVARIANT *property, VARTYPE vt)
{
    HRESULT hr;
    HMODULE propsys = LoadLibraryA("propsys.dll");
    pPropVariantChangeType = (void *)GetProcAddress(propsys, "PropVariantChangeType");

    if (!pPropVariantChangeType)
    {
        ERR("PropVariantChangeType function missing!\n");
        return ERROR_FUNCTION_FAILED;
    }

    hr = pPropVariantChangeType(changed, property, 0, vt);
    return (hr == S_OK) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
}

166
/* FIXME: doesn't deal with endian conversion */
167
static void read_properties_from_data( PROPVARIANT *prop, LPBYTE data, DWORD sz )
168 169
{
    UINT type;
170
    DWORD i, size;
171
    PROPERTY_DATA *propdata;
172 173
    PROPVARIANT property, *ptr;
    PROPVARIANT changed;
174 175 176 177 178
    PROPERTYIDOFFSET *idofs;
    PROPERTYSECTIONHEADER *section_hdr;

    section_hdr = (PROPERTYSECTIONHEADER*) &data[0];
    idofs = (PROPERTYIDOFFSET*) &data[SECT_HDR_SIZE];
179 180

    /* now set all the properties */
181
    for( i = 0; i < section_hdr->cProperties; i++ )
182
    {
183 184 185 186 187 188
        if( idofs[i].propid >= MSI_MAX_PROPS )
        {
            ERR("Unknown property ID %d\n", idofs[i].propid );
            break;
        }

189 190 191
        type = get_type( idofs[i].propid );
        if( type == VT_EMPTY )
        {
192
            ERR("propid %d has unknown type\n", idofs[i].propid);
193 194 195
            break;
        }

196
        propdata = (PROPERTY_DATA*) &data[ idofs[i].dwOffset ];
197 198 199 200

        /* check we don't run off the end of the data */
        size = sz - idofs[i].dwOffset - sizeof(DWORD);
        if( sizeof(DWORD) > size ||
201 202
            ( propdata->type == VT_FILETIME && sizeof(FILETIME) > size ) ||
            ( propdata->type == VT_LPSTR && (propdata->u.str.len + sizeof(DWORD)) > size ) )
203 204 205 206 207
        {
            ERR("not enough data\n");
            break;
        }

208 209
        property.vt = propdata->type;
        if( propdata->type == VT_LPSTR )
210
        {
211
            LPSTR str = msi_alloc( propdata->u.str.len );
212 213
            memcpy( str, propdata->u.str.str, propdata->u.str.len );
            str[ propdata->u.str.len - 1 ] = 0;
214 215 216 217 218 219 220 221 222 223 224 225 226 227
            property.u.pszVal = str;
        }
        else if( propdata->type == VT_FILETIME )
            property.u.filetime = propdata->u.ft;
        else if( propdata->type == VT_I2 )
            property.u.iVal = propdata->u.i2;
        else if( propdata->type == VT_I4 )
            property.u.lVal = propdata->u.i4;

        /* check the type is the same as we expect */
        if( type != propdata->type )
        {
            propvar_changetype(&changed, &property, type);
            ptr = &changed;
228
        }
229 230 231
        else
            ptr = &property;

232
        prop[ idofs[i].propid ] = *ptr;
233
    }
234
}
235

236 237 238 239 240 241 242 243 244 245
static UINT load_summary_info( MSISUMMARYINFO *si, IStream *stm )
{
    UINT ret = ERROR_FUNCTION_FAILED;
    PROPERTYSETHEADER set_hdr;
    FORMATIDOFFSET format_hdr;
    PROPERTYSECTIONHEADER section_hdr;
    LPBYTE data = NULL;
    LARGE_INTEGER ofs;
    ULONG count, sz;
    HRESULT r;
246

247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
    TRACE("%p %p\n", si, stm);

    /* read the header */
    sz = sizeof set_hdr;
    r = IStream_Read( stm, &set_hdr, sz, &count );
    if( FAILED(r) || count != sz )
        return ret;

    if( set_hdr.wByteOrder != 0xfffe )
    {
        ERR("property set not big-endian %04X\n", set_hdr.wByteOrder);
        return ret;
    }

    sz = sizeof format_hdr;
    r = IStream_Read( stm, &format_hdr, sz, &count );
    if( FAILED(r) || count != sz )
        return ret;

    /* check the format id is correct */
    if( !IsEqualGUID( &FMTID_SummaryInformation, &format_hdr.fmtid ) )
        return ret;

    /* seek to the location of the section */
    ofs.QuadPart = format_hdr.dwOffset;
    r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, NULL );
    if( FAILED(r) )
        return ret;

    /* read the section itself */
277
    sz = SECT_HDR_SIZE;
278 279 280 281 282 283
    r = IStream_Read( stm, &section_hdr, sz, &count );
    if( FAILED(r) || count != sz )
        return ret;

    if( section_hdr.cProperties > MSI_MAX_PROPS )
    {
284
        ERR("too many properties %d\n", section_hdr.cProperties);
285 286 287
        return ret;
    }

288
    data = msi_alloc( section_hdr.cbSection);
289
    if( !data )
290 291
        return ret;

292 293
    memcpy( data, &section_hdr, SECT_HDR_SIZE );

294
    /* read all the data in one go */
295 296
    sz = section_hdr.cbSection - SECT_HDR_SIZE;
    r = IStream_Read( stm, &data[ SECT_HDR_SIZE ], sz, &count );
297
    if( SUCCEEDED(r) && count == sz )
298 299
        read_properties_from_data( si->property, data, sz + SECT_HDR_SIZE );
    else
300
        ERR("failed to read properties %d %d\n", count, sz);
301

302
    msi_free( data );
303 304 305
    return ret;
}

306 307 308 309 310 311 312 313 314 315 316 317
static DWORD write_dword( LPBYTE data, DWORD ofs, DWORD val )
{
    if( data )
    {
        data[ofs++] = val&0xff;
        data[ofs++] = (val>>8)&0xff;
        data[ofs++] = (val>>16)&0xff;
        data[ofs++] = (val>>24)&0xff;
    }
    return 4;
}

318
static DWORD write_filetime( LPBYTE data, DWORD ofs, const FILETIME *ft )
319 320 321 322 323 324 325 326 327 328 329
{
    write_dword( data, ofs, ft->dwLowDateTime );
    write_dword( data, ofs + 4, ft->dwHighDateTime );
    return 8;
}

static DWORD write_string( LPBYTE data, DWORD ofs, LPCSTR str )
{
    DWORD len = lstrlenA( str ) + 1;
    write_dword( data, ofs, len );
    if( data )
330
        memcpy( &data[ofs + 4], str, len );
331 332 333
    return (7 + len) & ~3;
}

334
static UINT write_property_to_data( const PROPVARIANT *prop, LPBYTE data )
335
{
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
    DWORD sz = 0;

    if( prop->vt == VT_EMPTY )
        return sz;

    /* add the type */
    sz += write_dword( data, sz, prop->vt );
    switch( prop->vt )
    {
    case VT_I2:
        sz += write_dword( data, sz, prop->u.iVal );
        break;
    case VT_I4:
        sz += write_dword( data, sz, prop->u.lVal );
        break;
    case VT_FILETIME:
        sz += write_filetime( data, sz, &prop->u.filetime );
        break;
    case VT_LPSTR:
        sz += write_string( data, sz, prop->u.pszVal );
        break;
    }
    return sz;
}

361
static UINT save_summary_info( const MSISUMMARYINFO * si, IStream *stm )
362 363 364 365 366 367 368 369
{
    UINT ret = ERROR_FUNCTION_FAILED;
    PROPERTYSETHEADER set_hdr;
    FORMATIDOFFSET format_hdr;
    PROPERTYSECTIONHEADER section_hdr;
    PROPERTYIDOFFSET idofs[MSI_MAX_PROPS];
    LPBYTE data = NULL;
    ULONG count, sz;
370
    HRESULT r;
371
    int i;
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386

    /* write the header */
    sz = sizeof set_hdr;
    memset( &set_hdr, 0, sz );
    set_hdr.wByteOrder = 0xfffe;
    set_hdr.wFormat = 0;
    set_hdr.dwOSVer = 0x00020005; /* build 5, platform id 2 */
    /* set_hdr.clsID is {00000000-0000-0000-0000-000000000000} */
    set_hdr.reserved = 1;
    r = IStream_Write( stm, &set_hdr, sz, &count );
    if( FAILED(r) || count != sz )
        return ret;

    /* write the format header */
    sz = sizeof format_hdr;
387
    format_hdr.fmtid = FMTID_SummaryInformation;
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
    format_hdr.dwOffset = sizeof format_hdr + sizeof set_hdr;
    r = IStream_Write( stm, &format_hdr, sz, &count );
    if( FAILED(r) || count != sz )
        return ret;

    /* add up how much space the data will take and calculate the offsets */
    section_hdr.cbSection = sizeof section_hdr;
    section_hdr.cbSection += (get_property_count( si->property ) * sizeof idofs[0]);
    section_hdr.cProperties = 0;
    for( i = 0; i < MSI_MAX_PROPS; i++ )
    {
        sz = write_property_to_data( &si->property[i], NULL );
        if( !sz )
            continue;
        idofs[ section_hdr.cProperties ].propid = i;
        idofs[ section_hdr.cProperties ].dwOffset = section_hdr.cbSection;
        section_hdr.cProperties++;
        section_hdr.cbSection += sz;
    }

408
    data = msi_alloc_zero( section_hdr.cbSection );
409 410 411 412 413 414 415 416 417 418 419 420 421

    sz = 0;
    memcpy( &data[sz], &section_hdr, sizeof section_hdr );
    sz += sizeof section_hdr;

    memcpy( &data[sz], idofs, section_hdr.cProperties * sizeof idofs[0] );
    sz += section_hdr.cProperties * sizeof idofs[0];

    /* write out the data */
    for( i = 0; i < MSI_MAX_PROPS; i++ )
        sz += write_property_to_data( &si->property[i], &data[sz] );

    r = IStream_Write( stm, data, sz, &count );
422
    msi_free( data );
423 424 425 426 427 428
    if( FAILED(r) || count != sz )
        return ret;

    return ERROR_SUCCESS;
}

429
MSISUMMARYINFO *MSI_GetSummaryInformationW( IStorage *stg, UINT uiUpdateCount )
430 431 432
{
    IStream *stm = NULL;
    MSISUMMARYINFO *si;
433
    DWORD grfMode;
434
    HRESULT r;
435

436
    TRACE("%p %d\n", stg, uiUpdateCount );
437

438 439 440
    si = alloc_msiobject( MSIHANDLETYPE_SUMMARYINFO, 
                  sizeof (MSISUMMARYINFO), MSI_CloseSummaryInfo );
    if( !si )
441
        return si;
442

443
    si->update_count = uiUpdateCount;
444 445
    IStorage_AddRef( stg );
    si->storage = stg;
446

447 448
    /* read the stream... if we fail, we'll start with an empty property set */
    grfMode = STGM_READ | STGM_SHARE_EXCLUSIVE;
449
    r = IStorage_OpenStream( si->storage, szSumInfo, 0, grfMode, 0, &stm );
450
    if( SUCCEEDED(r) )
451
    {
452 453
        load_summary_info( si, stm );
        IStream_Release( stm );
454 455
    }

456 457 458 459 460 461 462 463 464 465
    return si;
}

UINT WINAPI MsiGetSummaryInformationW( MSIHANDLE hDatabase, 
              LPCWSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle )
{
    MSISUMMARYINFO *si;
    MSIDATABASE *db;
    UINT ret = ERROR_FUNCTION_FAILED;

466
    TRACE("%d %s %d %p\n", hDatabase, debugstr_w(szDatabase),
467 468 469 470 471
           uiUpdateCount, pHandle);

    if( !pHandle )
        return ERROR_INVALID_PARAMETER;

472
    if( szDatabase && szDatabase[0] )
473
    {
474 475 476
        LPCWSTR persist = uiUpdateCount ? MSIDBOPEN_TRANSACT : MSIDBOPEN_READONLY;

        ret = MSI_OpenDatabaseW( szDatabase, persist, &db );
477 478 479
        if( ret != ERROR_SUCCESS )
            return ret;
    }
480
    else
481 482 483
    {
        db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
        if( !db )
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
        {
            HRESULT hr;
            IWineMsiRemoteDatabase *remote_database;

            remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hDatabase );
            if ( !remote_database )
                return ERROR_INVALID_HANDLE;

            hr = IWineMsiRemoteDatabase_GetSummaryInformation( remote_database,
                                                               uiUpdateCount, pHandle );
            IWineMsiRemoteDatabase_Release( remote_database );

            if (FAILED(hr))
            {
                if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
                    return HRESULT_CODE(hr);

                return ERROR_FUNCTION_FAILED;
            }

            return ERROR_SUCCESS;
        }
506 507
    }

508
    si = MSI_GetSummaryInformationW( db->storage, uiUpdateCount );
509 510 511 512 513
    if (si)
    {
        *pHandle = alloc_msihandle( &si->hdr );
        if( *pHandle )
            ret = ERROR_SUCCESS;
514 515
        else
            ret = ERROR_NOT_ENOUGH_MEMORY;
516 517
        msiobj_release( &si->hdr );
    }
518

519
    msiobj_release( &db->hdr );
520 521 522
    return ret;
}

523 524
UINT WINAPI MsiGetSummaryInformationA(MSIHANDLE hDatabase, 
              LPCSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle)
525
{
526 527
    LPWSTR szwDatabase = NULL;
    UINT ret;
528

529
    TRACE("%d %s %d %p\n", hDatabase, debugstr_a(szDatabase),
530
          uiUpdateCount, pHandle);
531

532 533 534 535 536 537 538 539
    if( szDatabase )
    {
        szwDatabase = strdupAtoW( szDatabase );
        if( !szwDatabase )
            return ERROR_FUNCTION_FAILED;
    }

    ret = MsiGetSummaryInformationW(hDatabase, szwDatabase, uiUpdateCount, pHandle);
540

541
    msi_free( szwDatabase );
542 543

    return ret;
544 545
}

546
UINT WINAPI MsiSummaryInfoGetPropertyCount(MSIHANDLE hSummaryInfo, PUINT pCount)
547
{
548
    MSISUMMARYINFO *si;
549

550
    TRACE("%d %p\n", hSummaryInfo, pCount);
551

552 553
    si = msihandle2msiinfo( hSummaryInfo, MSIHANDLETYPE_SUMMARYINFO );
    if( !si )
554 555
        return ERROR_INVALID_HANDLE;

556 557 558
    if( pCount )
        *pCount = get_property_count( si->property );
    msiobj_release( &si->hdr );
559

560 561 562 563 564 565 566 567
    return ERROR_SUCCESS;
}

static UINT get_prop( MSIHANDLE handle, UINT uiProperty, UINT *puiDataType,
          INT *piValue, FILETIME *pftValue, awstring *str, DWORD *pcchValueBuf)
{
    MSISUMMARYINFO *si;
    PROPVARIANT *prop;
568
    UINT ret = ERROR_SUCCESS;
569

570
    TRACE("%d %d %p %p %p %p %p\n", handle, uiProperty, puiDataType,
571
          piValue, pftValue, str, pcchValueBuf);
572

573 574
    if ( uiProperty >= MSI_MAX_PROPS )
    {
575 576
        if (puiDataType) *puiDataType = VT_EMPTY;
        return ERROR_UNKNOWN_PROPERTY;
577 578
    }

579 580 581 582
    si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
    if( !si )
        return ERROR_INVALID_HANDLE;

583 584
    prop = &si->property[uiProperty];

585 586 587 588
    if( puiDataType )
        *puiDataType = prop->vt;

    switch( prop->vt )
589
    {
590 591 592 593
    case VT_I2:
        if( piValue )
            *piValue = prop->u.iVal;
        break;
594 595
    case VT_I4:
        if( piValue )
596
            *piValue = prop->u.lVal;
597 598
        break;
    case VT_LPSTR:
599
        if( pcchValueBuf )
600
        {
601 602 603 604
            DWORD len = 0;

            if( str->unicode )
            {
605 606
                len = MultiByteToWideChar( CP_ACP, 0, prop->u.pszVal, -1, NULL, 0 ) - 1;
                MultiByteToWideChar( CP_ACP, 0, prop->u.pszVal, -1, str->str.w, *pcchValueBuf );
607 608 609 610 611 612 613
            }
            else
            {
                len = lstrlenA( prop->u.pszVal );
                if( str->str.a )
                    lstrcpynA(str->str.a, prop->u.pszVal, *pcchValueBuf );
            }
614 615
            if (len >= *pcchValueBuf)
                ret = ERROR_MORE_DATA;
616
            *pcchValueBuf = len;
617 618 619 620
        }
        break;
    case VT_FILETIME:
        if( pftValue )
621
            *pftValue = prop->u.filetime;
622 623 624 625 626 627 628
        break;
    case VT_EMPTY:
        break;
    default:
        FIXME("Unknown property variant type\n");
        break;
    }
629
    msiobj_release( &si->hdr );
630
    return ret;
631 632
}

633 634 635 636 637 638 639 640 641 642 643 644
LPWSTR msi_suminfo_dup_string( MSISUMMARYINFO *si, UINT uiProperty )
{
    PROPVARIANT *prop;

    if ( uiProperty >= MSI_MAX_PROPS )
        return NULL;
    prop = &si->property[uiProperty];
    if( prop->vt != VT_LPSTR )
        return NULL;
    return strdupAtoW( prop->u.pszVal );
}

645 646 647 648 649 650 651 652 653 654 655 656
INT msi_suminfo_get_int32( MSISUMMARYINFO *si, UINT uiProperty )
{
    PROPVARIANT *prop;

    if ( uiProperty >= MSI_MAX_PROPS )
        return -1;
    prop = &si->property[uiProperty];
    if( prop->vt != VT_I4 )
        return -1;
    return prop->u.lVal;
}

657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
LPWSTR msi_get_suminfo_product( IStorage *stg )
{
    MSISUMMARYINFO *si;
    LPWSTR prod;

    si = MSI_GetSummaryInformationW( stg, 0 );
    if (!si)
    {
        ERR("no summary information!\n");
        return NULL;
    }
    prod = msi_suminfo_dup_string( si, PID_REVNUMBER );
    msiobj_release( &si->hdr );
    return prod;
}

673
UINT WINAPI MsiSummaryInfoGetPropertyA(
674 675
      MSIHANDLE handle, UINT uiProperty, PUINT puiDataType, LPINT piValue,
      FILETIME *pftValue, LPSTR szValueBuf, LPDWORD pcchValueBuf)
676 677 678
{
    awstring str;

679
    TRACE("%d %d %p %p %p %p %p\n", handle, uiProperty, puiDataType,
680 681 682 683 684 685 686
          piValue, pftValue, szValueBuf, pcchValueBuf );

    str.unicode = FALSE;
    str.str.a = szValueBuf;

    return get_prop( handle, uiProperty, puiDataType, piValue,
                     pftValue, &str, pcchValueBuf );
687 688 689
}

UINT WINAPI MsiSummaryInfoGetPropertyW(
690 691
      MSIHANDLE handle, UINT uiProperty, PUINT puiDataType, LPINT piValue,
      FILETIME *pftValue, LPWSTR szValueBuf, LPDWORD pcchValueBuf)
692
{
693 694
    awstring str;

695
    TRACE("%d %d %p %p %p %p %p\n", handle, uiProperty, puiDataType,
696
          piValue, pftValue, szValueBuf, pcchValueBuf );
697

698 699
    str.unicode = TRUE;
    str.str.w = szValueBuf;
700

701 702 703 704
    return get_prop( handle, uiProperty, puiDataType, piValue,
                     pftValue, &str, pcchValueBuf );
}

705
static UINT set_prop( MSISUMMARYINFO *si, UINT uiProperty, UINT type,
706
               INT iValue, FILETIME* pftValue, awcstring *str )
707 708
{
    PROPVARIANT *prop;
709
    UINT len;
710

711 712
    TRACE("%p %u %u %i %p %p\n", si, uiProperty, type, iValue,
          pftValue, str );
713

714
    prop = &si->property[uiProperty];
715

716
    if( prop->vt == VT_EMPTY )
717
    {
718
        if( !si->update_count )
719 720
            return ERROR_FUNCTION_FAILED;

721
        si->update_count--;
722
    }
723
    else if( prop->vt != type )
724
        return ERROR_SUCCESS;
725

726 727
    free_prop( prop );
    prop->vt = type;
728
    switch( type )
729 730
    {
    case VT_I4:
731
        prop->u.lVal = iValue;
732
        break;
733 734
    case VT_I2:
        prop->u.iVal = iValue;
735 736
        break;
    case VT_FILETIME:
737
        prop->u.filetime = *pftValue;
738
        break;
739 740 741 742 743
    case VT_LPSTR:
        if( str->unicode )
        {
            len = WideCharToMultiByte( CP_ACP, 0, str->str.w, -1,
                                       NULL, 0, NULL, NULL );
744
            prop->u.pszVal = msi_alloc( len );
745 746 747 748 749 750
            WideCharToMultiByte( CP_ACP, 0, str->str.w, -1,
                                 prop->u.pszVal, len, NULL, NULL );
        }
        else
        {
            len = lstrlenA( str->str.a ) + 1;
751
            prop->u.pszVal = msi_alloc( len );
752 753
            lstrcpyA( prop->u.pszVal, str->str.a );
        }
754 755 756
        break;
    }

757
    return ERROR_SUCCESS;
758
}
759

760
UINT WINAPI MsiSummaryInfoSetPropertyW( MSIHANDLE handle, UINT uiProperty,
761
               UINT uiDataType, INT iValue, FILETIME* pftValue, LPCWSTR szValue )
762
{
763
    awcstring str;
764 765
    MSISUMMARYINFO *si;
    UINT type, ret;
766

767
    TRACE("%d %u %u %i %p %s\n", handle, uiProperty, uiDataType,
768 769
          iValue, pftValue, debugstr_w(szValue) );

770 771 772 773 774 775 776 777 778 779 780 781 782 783
    type = get_type( uiProperty );
    if( type == VT_EMPTY || type != uiDataType )
        return ERROR_DATATYPE_MISMATCH;

    if( uiDataType == VT_LPSTR && !szValue )
        return ERROR_INVALID_PARAMETER;

    if( uiDataType == VT_FILETIME && !pftValue )
        return ERROR_INVALID_PARAMETER;

    si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
    if( !si )
        return ERROR_INVALID_HANDLE;

784 785
    str.unicode = TRUE;
    str.str.w = szValue;
786 787 788 789
    ret = set_prop( si, uiProperty, type, iValue, pftValue, &str );

    msiobj_release( &si->hdr );
    return ret;
790 791
}

792
UINT WINAPI MsiSummaryInfoSetPropertyA( MSIHANDLE handle, UINT uiProperty,
793
               UINT uiDataType, INT iValue, FILETIME* pftValue, LPCSTR szValue )
794
{
795
    awcstring str;
796 797
    MSISUMMARYINFO *si;
    UINT type, ret;
798

799
    TRACE("%d %u %u %i %p %s\n", handle, uiProperty, uiDataType,
800 801
          iValue, pftValue, debugstr_a(szValue) );

802 803 804 805 806 807 808 809 810 811 812 813 814 815
    type = get_type( uiProperty );
    if( type == VT_EMPTY || type != uiDataType )
        return ERROR_DATATYPE_MISMATCH;

    if( uiDataType == VT_LPSTR && !szValue )
        return ERROR_INVALID_PARAMETER;

    if( uiDataType == VT_FILETIME && !pftValue )
        return ERROR_INVALID_PARAMETER;

    si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
    if( !si )
        return ERROR_INVALID_HANDLE;

816 817
    str.unicode = FALSE;
    str.str.a = szValue;
818 819 820 821
    ret = set_prop( si, uiProperty, uiDataType, iValue, pftValue, &str );

    msiobj_release( &si->hdr );
    return ret;
822 823
}

824
static UINT suminfo_persist( MSISUMMARYINFO *si )
825
{
826
    UINT ret = ERROR_FUNCTION_FAILED;
827 828 829 830 831
    IStream *stm = NULL;
    DWORD grfMode;
    HRESULT r;

    grfMode = STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE;
832
    r = IStorage_CreateStream( si->storage, szSumInfo, grfMode, 0, 0, &stm );
833 834 835 836 837
    if( SUCCEEDED(r) )
    {
        ret = save_summary_info( si, stm );
        IStream_Release( stm );
    }
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957
    return ret;
}

static void parse_filetime( LPCWSTR str, FILETIME *ft )
{
    SYSTEMTIME lt, utc;
    const WCHAR *p = str;
    WCHAR *end;

    memset( &lt, 0, sizeof(lt) );

    /* YYYY/MM/DD hh:mm:ss */

    while (isspaceW( *p )) p++;

    lt.wYear = strtolW( p, &end, 10 );
    if (*end != '/') return;
    p = end + 1;

    lt.wMonth = strtolW( p, &end, 10 );
    if (*end != '/') return;
    p = end + 1;

    lt.wDay = strtolW( p, &end, 10 );
    if (*end != ' ') return;
    p = end + 1;

    while (isspaceW( *p )) p++;

    lt.wHour = strtolW( p, &end, 10 );
    if (*end != ':') return;
    p = end + 1;

    lt.wMinute = strtolW( p, &end, 10 );
    if (*end != ':') return;
    p = end + 1;

    lt.wSecond = strtolW( p, &end, 10 );

    TzSpecificLocalTimeToSystemTime( NULL, &lt, &utc );
    SystemTimeToFileTime( &utc, ft );
}

static UINT parse_prop( LPCWSTR prop, LPCWSTR value, UINT *pid, INT *int_value,
                        FILETIME *ft_value, awcstring *str_value )
{
    *pid = atoiW( prop );
    switch (*pid)
    {
    case PID_CODEPAGE:
    case PID_WORDCOUNT:
    case PID_CHARCOUNT:
    case PID_SECURITY:
    case PID_PAGECOUNT:
        *int_value = atoiW( value );
        break;

    case PID_LASTPRINTED:
    case PID_CREATE_DTM:
    case PID_LASTSAVE_DTM:
        parse_filetime( value, ft_value );
        break;

    case PID_SUBJECT:
    case PID_AUTHOR:
    case PID_KEYWORDS:
    case PID_COMMENTS:
    case PID_TEMPLATE:
    case PID_LASTAUTHOR:
    case PID_REVNUMBER:
    case PID_APPNAME:
    case PID_TITLE:
        str_value->str.w = value;
        str_value->unicode = TRUE;
        break;

    default:
        WARN("unhandled prop id %u\n", *pid);
        return ERROR_FUNCTION_FAILED;
    }

    return ERROR_SUCCESS;
}

UINT msi_add_suminfo( MSIDATABASE *db, LPWSTR **records, int num_records, int num_columns )
{
    UINT r = ERROR_FUNCTION_FAILED;
    DWORD i, j;
    MSISUMMARYINFO *si;

    si = MSI_GetSummaryInformationW( db->storage, num_records * (num_columns / 2) );
    if (!si)
    {
        ERR("no summary information!\n");
        return ERROR_FUNCTION_FAILED;
    }

    for (i = 0; i < num_records; i++)
    {
        for (j = 0; j < num_columns; j += 2)
        {
            UINT pid;
            INT int_value = 0;
            FILETIME ft_value;
            awcstring str_value;

            r = parse_prop( records[i][j], records[i][j + 1], &pid, &int_value, &ft_value, &str_value );
            if (r != ERROR_SUCCESS)
                goto end;

            r = set_prop( si, pid, get_type(pid), int_value, &ft_value, &str_value );
            if (r != ERROR_SUCCESS)
                goto end;
        }
    }

end:
    if (r == ERROR_SUCCESS)
        r = suminfo_persist( si );

958
    msiobj_release( &si->hdr );
959 960 961 962 963 964 965 966 967
    return r;
}

UINT WINAPI MsiSummaryInfoPersist( MSIHANDLE handle )
{
    MSISUMMARYINFO *si;
    UINT ret;

    TRACE("%d\n", handle );
968

969 970 971 972 973 974 975
    si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
    if( !si )
        return ERROR_INVALID_HANDLE;

    ret = suminfo_persist( si );

    msiobj_release( &si->hdr );
976
    return ret;
977
}
978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998

UINT WINAPI MsiCreateTransformSummaryInfoA( MSIHANDLE db, MSIHANDLE db_ref, LPCSTR transform, int error, int validation )
{
    UINT r;
    WCHAR *transformW = NULL;

    TRACE("%u, %u, %s, %d, %d\n", db, db_ref, debugstr_a(transform), error, validation);

    if (transform && !(transformW = strdupAtoW( transform )))
        return ERROR_OUTOFMEMORY;

    r = MsiCreateTransformSummaryInfoW( db, db_ref, transformW, error, validation );
    msi_free( transformW );
    return r;
}

UINT WINAPI MsiCreateTransformSummaryInfoW( MSIHANDLE db, MSIHANDLE db_ref, LPCWSTR transform, int error, int validation )
{
    FIXME("%u, %u, %s, %d, %d\n", db, db_ref, debugstr_w(transform), error, validation);
    return ERROR_FUNCTION_FAILED;
}