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