xref: /reactos/dll/win32/msi/suminfo.c (revision 48cc7814)
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, &section_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, &section_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], &section_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( &lt, 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, &lt, &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