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