xref: /reactos/dll/win32/msi/suminfo.c (revision d2aeaba5)
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 
25 #include "stdio.h"
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winreg.h"
29 #include "winnls.h"
30 #include "shlwapi.h"
31 #include "wine/debug.h"
32 #include "wine/exception.h"
33 #include "msi.h"
34 #include "msiquery.h"
35 #include "msidefs.h"
36 #include "objidl.h"
37 #include "propvarutil.h"
38 
39 #include "msipriv.h"
40 #include "winemsi_s.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->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 %lu\n", idofs[i].propid );
201             break;
202         }
203 
204         type = get_type( idofs[i].propid );
205         if( type == VT_EMPTY )
206         {
207             ERR( "propid %lu 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.pszVal = str;
230         }
231         else if( propdata->type == VT_FILETIME )
232             property.filetime = propdata->u.ft;
233         else if( propdata->type == VT_I2 )
234             property.iVal = propdata->u.i2;
235         else if( propdata->type == VT_I4 )
236             property.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 %lu\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 %lu %lu\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->iVal );
361         break;
362     case VT_I4:
363         sz += write_dword( data, sz, prop->lVal );
364         break;
365     case VT_FILETIME:
366         sz += write_filetime( data, sz, &prop->filetime );
367         break;
368     case VT_LPSTR:
369         sz += write_string( data, sz, prop->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, L"\5SummaryInformation", 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, L"\5SummaryInformation", &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, const WCHAR *szDatabase, UINT uiUpdateCount,
515                                        MSIHANDLE *pHandle )
516 {
517     MSISUMMARYINFO *si;
518     MSIDATABASE *db;
519     UINT ret;
520 
521     TRACE( "%lu, %s, %u, %p\n", hDatabase, debugstr_w(szDatabase), uiUpdateCount, pHandle );
522 
523     if( !pHandle )
524         return ERROR_INVALID_PARAMETER;
525 
526     if( szDatabase && szDatabase[0] )
527     {
528         LPCWSTR persist = uiUpdateCount ? MSIDBOPEN_TRANSACT : MSIDBOPEN_READONLY;
529 
530         ret = MSI_OpenDatabaseW( szDatabase, persist, &db );
531         if( ret != ERROR_SUCCESS )
532             return ret;
533     }
534     else
535     {
536         db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
537         if( !db )
538         {
539             MSIHANDLE remote, remote_suminfo;
540 
541             if (!(remote = msi_get_remote(hDatabase)))
542                 return ERROR_INVALID_HANDLE;
543 
544             __TRY
545             {
546                 ret = remote_DatabaseGetSummaryInformation(remote, uiUpdateCount, &remote_suminfo);
547             }
548             __EXCEPT(rpc_filter)
549             {
550                 ret = GetExceptionCode();
551             }
552             __ENDTRY
553 
554             if (!ret)
555                 *pHandle = alloc_msi_remote_handle(remote_suminfo);
556 
557             return ret;
558         }
559     }
560 
561     ret = msi_get_suminfo( db->storage, uiUpdateCount, &si );
562     if (ret != ERROR_SUCCESS)
563         ret = msi_get_db_suminfo( db, uiUpdateCount, &si );
564     if (ret != ERROR_SUCCESS)
565     {
566         if ((si = create_suminfo( db->storage, uiUpdateCount )))
567             ret = ERROR_SUCCESS;
568     }
569 
570     if (ret == ERROR_SUCCESS)
571     {
572         *pHandle = alloc_msihandle( &si->hdr );
573         if( *pHandle )
574             ret = ERROR_SUCCESS;
575         else
576             ret = ERROR_NOT_ENOUGH_MEMORY;
577         msiobj_release( &si->hdr );
578     }
579 
580     msiobj_release( &db->hdr );
581     return ret;
582 }
583 
584 UINT WINAPI MsiGetSummaryInformationA( MSIHANDLE hDatabase, const char *szDatabase, UINT uiUpdateCount,
585                                        MSIHANDLE *pHandle )
586 {
587     WCHAR *szwDatabase = NULL;
588     UINT ret;
589 
590     TRACE( "%lu, %s, %u, %p\n", hDatabase, debugstr_a(szDatabase), uiUpdateCount, pHandle );
591 
592     if( szDatabase )
593     {
594         szwDatabase = strdupAtoW( szDatabase );
595         if( !szwDatabase )
596             return ERROR_FUNCTION_FAILED;
597     }
598 
599     ret = MsiGetSummaryInformationW(hDatabase, szwDatabase, uiUpdateCount, pHandle);
600 
601     msi_free( szwDatabase );
602 
603     return ret;
604 }
605 
606 UINT WINAPI MsiSummaryInfoGetPropertyCount( MSIHANDLE hSummaryInfo, UINT *pCount )
607 {
608     MSISUMMARYINFO *si;
609 
610     TRACE( "%lu, %p\n", hSummaryInfo, pCount );
611 
612     si = msihandle2msiinfo( hSummaryInfo, MSIHANDLETYPE_SUMMARYINFO );
613     if( !si )
614     {
615         MSIHANDLE remote;
616         UINT ret;
617 
618         if (!(remote = msi_get_remote( hSummaryInfo )))
619             return ERROR_INVALID_HANDLE;
620 
621         __TRY
622         {
623             ret = remote_SummaryInfoGetPropertyCount( remote, pCount );
624         }
625         __EXCEPT(rpc_filter)
626         {
627             ret = GetExceptionCode();
628         }
629         __ENDTRY
630 
631         return ret;
632     }
633 
634     if( pCount )
635         *pCount = get_property_count( si->property );
636     msiobj_release( &si->hdr );
637 
638     return ERROR_SUCCESS;
639 }
640 
641 static UINT get_prop( MSISUMMARYINFO *si, UINT uiProperty, UINT *puiDataType, INT *piValue,
642                       FILETIME *pftValue, awstring *str, DWORD *pcchValueBuf)
643 {
644     PROPVARIANT *prop;
645     UINT ret = ERROR_SUCCESS;
646 
647     prop = &si->property[uiProperty];
648 
649     if( puiDataType )
650         *puiDataType = prop->vt;
651 
652     switch( prop->vt )
653     {
654     case VT_I2:
655         if( piValue )
656             *piValue = prop->iVal;
657         break;
658     case VT_I4:
659         if( piValue )
660             *piValue = prop->lVal;
661         break;
662     case VT_LPSTR:
663         if( pcchValueBuf )
664         {
665             DWORD len = 0;
666 
667             if( str->unicode )
668             {
669                 len = MultiByteToWideChar( CP_ACP, 0, prop->pszVal, -1, NULL, 0 ) - 1;
670                 MultiByteToWideChar( CP_ACP, 0, prop->pszVal, -1, str->str.w, *pcchValueBuf );
671             }
672             else
673             {
674                 len = lstrlenA( prop->pszVal );
675                 if( str->str.a )
676                     lstrcpynA(str->str.a, prop->pszVal, *pcchValueBuf );
677             }
678             if (len >= *pcchValueBuf)
679                 ret = ERROR_MORE_DATA;
680             *pcchValueBuf = len;
681         }
682         break;
683     case VT_FILETIME:
684         if( pftValue )
685             *pftValue = prop->filetime;
686         break;
687     case VT_EMPTY:
688         break;
689     default:
690         FIXME("Unknown property variant type\n");
691         break;
692     }
693     return ret;
694 }
695 
696 LPWSTR msi_suminfo_dup_string( MSISUMMARYINFO *si, UINT uiProperty )
697 {
698     PROPVARIANT *prop;
699 
700     if ( uiProperty >= MSI_MAX_PROPS )
701         return NULL;
702     prop = &si->property[uiProperty];
703     if( prop->vt != VT_LPSTR )
704         return NULL;
705     return strdupAtoW( prop->pszVal );
706 }
707 
708 INT msi_suminfo_get_int32( MSISUMMARYINFO *si, UINT uiProperty )
709 {
710     PROPVARIANT *prop;
711 
712     if ( uiProperty >= MSI_MAX_PROPS )
713         return -1;
714     prop = &si->property[uiProperty];
715     if( prop->vt != VT_I4 )
716         return -1;
717     return prop->lVal;
718 }
719 
720 LPWSTR msi_get_suminfo_product( IStorage *stg )
721 {
722     MSISUMMARYINFO *si;
723     LPWSTR prod;
724     UINT r;
725 
726     r = msi_get_suminfo( stg, 0, &si );
727     if (r != ERROR_SUCCESS)
728     {
729         ERR("no summary information!\n");
730         return NULL;
731     }
732     prod = msi_suminfo_dup_string( si, PID_REVNUMBER );
733     msiobj_release( &si->hdr );
734     return prod;
735 }
736 
737 UINT WINAPI MsiSummaryInfoGetPropertyA( MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, INT *piValue,
738                                         FILETIME *pftValue, char *szValueBuf, DWORD *pcchValueBuf )
739 {
740     MSISUMMARYINFO *si;
741     awstring str;
742     UINT r;
743 
744     TRACE( "%lu, %u, %p, %p, %p, %p, %p\n", handle, uiProperty, puiDataType, piValue, pftValue, szValueBuf,
745            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     {
755         MSIHANDLE remote;
756         WCHAR *buf = NULL;
757 
758         if (!(remote = msi_get_remote( handle )))
759             return ERROR_INVALID_HANDLE;
760 
761         __TRY
762         {
763             r = remote_SummaryInfoGetProperty( remote, uiProperty, puiDataType, piValue, pftValue, &buf );
764         }
765         __EXCEPT(rpc_filter)
766         {
767             r = GetExceptionCode();
768         }
769         __ENDTRY
770 
771         if (!r && buf)
772         {
773             r = msi_strncpyWtoA( buf, -1, szValueBuf, pcchValueBuf, TRUE );
774         }
775 
776         midl_user_free( buf );
777         return r;
778     }
779 
780     str.unicode = FALSE;
781     str.str.a = szValueBuf;
782 
783     r = get_prop( si, uiProperty, puiDataType, piValue, pftValue, &str, pcchValueBuf );
784     msiobj_release( &si->hdr );
785     return r;
786 }
787 
788 UINT WINAPI MsiSummaryInfoGetPropertyW( MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, INT *piValue,
789                                         FILETIME *pftValue, WCHAR *szValueBuf, DWORD *pcchValueBuf )
790 {
791     MSISUMMARYINFO *si;
792     awstring str;
793     UINT r;
794 
795     TRACE( "%lu, %u, %p, %p, %p, %p, %p\n", handle, uiProperty, puiDataType, piValue, pftValue, szValueBuf,
796            pcchValueBuf );
797 
798     if (uiProperty >= MSI_MAX_PROPS)
799     {
800         if (puiDataType) *puiDataType = VT_EMPTY;
801         return ERROR_UNKNOWN_PROPERTY;
802     }
803 
804     if (!(si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO )))
805     {
806         MSIHANDLE remote;
807         WCHAR *buf = NULL;
808 
809         if (!(remote = msi_get_remote( handle )))
810             return ERROR_INVALID_HANDLE;
811 
812         __TRY
813         {
814             r = remote_SummaryInfoGetProperty( remote, uiProperty, puiDataType, piValue, pftValue, &buf );
815         }
816         __EXCEPT(rpc_filter)
817         {
818             r = GetExceptionCode();
819         }
820         __ENDTRY
821 
822         if (!r && buf)
823             r = msi_strncpyW( buf, -1, szValueBuf, pcchValueBuf );
824 
825         midl_user_free( buf );
826         return r;
827     }
828 
829     str.unicode = TRUE;
830     str.str.w = szValueBuf;
831 
832     r = get_prop( si, uiProperty, puiDataType, piValue, pftValue, &str, pcchValueBuf );
833     msiobj_release( &si->hdr );
834     return r;
835 }
836 
837 static UINT set_prop( MSISUMMARYINFO *si, UINT uiProperty, UINT type,
838                       INT iValue, FILETIME *pftValue, awcstring *str )
839 {
840     PROPVARIANT *prop;
841     UINT len;
842 
843     TRACE("%p, %u, %u, %d, %p, %p\n", si, uiProperty, type, iValue, pftValue, str );
844 
845     prop = &si->property[uiProperty];
846 
847     if( prop->vt == VT_EMPTY )
848     {
849         if( !si->update_count )
850             return ERROR_FUNCTION_FAILED;
851 
852         si->update_count--;
853     }
854     else if( prop->vt != type )
855         return ERROR_SUCCESS;
856 
857     free_prop( prop );
858     prop->vt = type;
859     switch( type )
860     {
861     case VT_I4:
862         prop->lVal = iValue;
863         break;
864     case VT_I2:
865         prop->iVal = iValue;
866         break;
867     case VT_FILETIME:
868         prop->filetime = *pftValue;
869         break;
870     case VT_LPSTR:
871         if( str->unicode )
872         {
873             len = WideCharToMultiByte( CP_ACP, 0, str->str.w, -1,
874                                        NULL, 0, NULL, NULL );
875             prop->pszVal = msi_alloc( len );
876             WideCharToMultiByte( CP_ACP, 0, str->str.w, -1,
877                                  prop->pszVal, len, NULL, NULL );
878         }
879         else
880         {
881             len = lstrlenA( str->str.a ) + 1;
882             prop->pszVal = msi_alloc( len );
883             lstrcpyA( prop->pszVal, str->str.a );
884         }
885         break;
886     }
887 
888     return ERROR_SUCCESS;
889 }
890 
891 static UINT msi_set_prop( MSISUMMARYINFO *si, UINT uiProperty, UINT uiDataType,
892                           INT iValue, FILETIME *pftValue, awcstring *str )
893 {
894     UINT type = get_type( uiProperty );
895     if( type == VT_EMPTY || type != uiDataType )
896         return ERROR_DATATYPE_MISMATCH;
897 
898     if( uiDataType == VT_LPSTR && !str->str.a )
899         return ERROR_INVALID_PARAMETER;
900 
901     if( uiDataType == VT_FILETIME && !pftValue )
902         return ERROR_INVALID_PARAMETER;
903 
904     return set_prop( si, uiProperty, type, iValue, pftValue, str );
905 }
906 
907 UINT WINAPI MsiSummaryInfoSetPropertyW( MSIHANDLE handle, UINT uiProperty, UINT uiDataType, INT iValue,
908                                         FILETIME *pftValue, const WCHAR *szValue )
909 {
910     awcstring str;
911     MSISUMMARYINFO *si;
912     UINT ret;
913 
914     TRACE( "%lu, %u, %u, %d, %p, %s\n", handle, uiProperty, uiDataType, iValue, pftValue, debugstr_w(szValue) );
915 
916     if (!(si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO )))
917     {
918         MSIHANDLE remote;
919 
920         if ((remote = msi_get_remote( handle )))
921         {
922             WARN("MsiSummaryInfoSetProperty not allowed during a custom action!\n");
923             return ERROR_FUNCTION_FAILED;
924         }
925 
926         return ERROR_INVALID_HANDLE;
927     }
928 
929     str.unicode = TRUE;
930     str.str.w = szValue;
931 
932     ret = msi_set_prop( si, uiProperty, uiDataType, iValue, pftValue, &str );
933     msiobj_release( &si->hdr );
934     return ret;
935 }
936 
937 UINT WINAPI MsiSummaryInfoSetPropertyA( MSIHANDLE handle, UINT uiProperty, UINT uiDataType, INT iValue,
938                                         FILETIME *pftValue, const char *szValue )
939 {
940     awcstring str;
941     MSISUMMARYINFO *si;
942     UINT ret;
943 
944     TRACE( "%lu, %u, %u, %d, %p, %s\n", handle, uiProperty, uiDataType, iValue, pftValue, debugstr_a(szValue) );
945 
946     if (!(si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO )))
947     {
948         MSIHANDLE remote;
949 
950         if ((remote = msi_get_remote( handle )))
951         {
952             WARN("MsiSummaryInfoSetProperty not allowed during a custom action!\n");
953             return ERROR_FUNCTION_FAILED;
954         }
955 
956         return ERROR_INVALID_HANDLE;
957     }
958 
959     str.unicode = FALSE;
960     str.str.a = szValue;
961 
962     ret = msi_set_prop( si, uiProperty, uiDataType, iValue, pftValue, &str );
963     msiobj_release( &si->hdr );
964     return ret;
965 }
966 
967 static UINT suminfo_persist( MSISUMMARYINFO *si )
968 {
969     UINT ret = ERROR_FUNCTION_FAILED;
970     IStream *stm = NULL;
971     DWORD grfMode;
972     HRESULT r;
973 
974     grfMode = STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE;
975     r = IStorage_CreateStream( si->storage, L"\5SummaryInformation", grfMode, 0, 0, &stm );
976     if( SUCCEEDED(r) )
977     {
978         ret = save_summary_info( si, stm );
979         IStream_Release( stm );
980     }
981     return ret;
982 }
983 
984 static void parse_filetime( LPCWSTR str, FILETIME *ft )
985 {
986     SYSTEMTIME lt, utc;
987     const WCHAR *p = str;
988     WCHAR *end;
989 
990     memset( &lt, 0, sizeof(lt) );
991 
992     /* YYYY/MM/DD hh:mm:ss */
993 
994     while (iswspace( *p )) p++;
995 
996     lt.wYear = wcstol( p, &end, 10 );
997     if (*end != '/') return;
998     p = end + 1;
999 
1000     lt.wMonth = wcstol( p, &end, 10 );
1001     if (*end != '/') return;
1002     p = end + 1;
1003 
1004     lt.wDay = wcstol( p, &end, 10 );
1005     if (*end != ' ') return;
1006     p = end + 1;
1007 
1008     while (iswspace( *p )) p++;
1009 
1010     lt.wHour = wcstol( p, &end, 10 );
1011     if (*end != ':') return;
1012     p = end + 1;
1013 
1014     lt.wMinute = wcstol( p, &end, 10 );
1015     if (*end != ':') return;
1016     p = end + 1;
1017 
1018     lt.wSecond = wcstol( p, &end, 10 );
1019 
1020     TzSpecificLocalTimeToSystemTime( NULL, &lt, &utc );
1021     SystemTimeToFileTime( &utc, ft );
1022 }
1023 
1024 static UINT parse_prop( LPCWSTR prop, LPCWSTR value, UINT *pid, INT *int_value,
1025                         FILETIME *ft_value, awcstring *str_value )
1026 {
1027     *pid = wcstol( prop, NULL, 10);
1028     switch (*pid)
1029     {
1030 #ifdef __REACTOS__
1031     case PID_CODEPAGE_MSI:
1032 #else
1033     case PID_CODEPAGE:
1034 #endif
1035     case PID_WORDCOUNT:
1036     case PID_CHARCOUNT:
1037 #ifdef __REACTOS__
1038     case PID_SECURITY_MSI:
1039 #else
1040     case PID_SECURITY:
1041 #endif
1042     case PID_PAGECOUNT:
1043         *int_value = wcstol( value, NULL, 10);
1044         break;
1045 
1046     case PID_LASTPRINTED:
1047     case PID_CREATE_DTM:
1048     case PID_LASTSAVE_DTM:
1049         parse_filetime( value, ft_value );
1050         break;
1051 
1052     case PID_SUBJECT:
1053     case PID_AUTHOR:
1054     case PID_KEYWORDS:
1055     case PID_COMMENTS:
1056     case PID_TEMPLATE:
1057     case PID_LASTAUTHOR:
1058     case PID_REVNUMBER:
1059     case PID_APPNAME:
1060     case PID_TITLE:
1061         str_value->str.w = value;
1062         str_value->unicode = TRUE;
1063         break;
1064 
1065     default:
1066         WARN("unhandled prop id %u\n", *pid);
1067         return ERROR_FUNCTION_FAILED;
1068     }
1069 
1070     return ERROR_SUCCESS;
1071 }
1072 
1073 UINT msi_add_suminfo( MSIDATABASE *db, LPWSTR **records, int num_records, int num_columns )
1074 {
1075     UINT r;
1076     int i, j;
1077     MSISUMMARYINFO *si;
1078 
1079     r = msi_get_suminfo( db->storage, num_records * (num_columns / 2), &si );
1080     if (r != ERROR_SUCCESS)
1081     {
1082         if (!(si = create_suminfo( db->storage, num_records * (num_columns / 2) )))
1083             return ERROR_OUTOFMEMORY;
1084         r = ERROR_SUCCESS;
1085     }
1086 
1087     for (i = 0; i < num_records; i++)
1088     {
1089         for (j = 0; j < num_columns; j += 2)
1090         {
1091             UINT pid;
1092             INT int_value = 0;
1093             FILETIME ft_value;
1094             awcstring str_value;
1095 
1096             r = parse_prop( records[i][j], records[i][j + 1], &pid, &int_value, &ft_value, &str_value );
1097             if (r != ERROR_SUCCESS)
1098                 goto end;
1099 
1100             r = set_prop( si, pid, get_type(pid), int_value, &ft_value, &str_value );
1101             if (r != ERROR_SUCCESS)
1102                 goto end;
1103         }
1104     }
1105 
1106 end:
1107     if (r == ERROR_SUCCESS)
1108         r = suminfo_persist( si );
1109 
1110     msiobj_release( &si->hdr );
1111     return r;
1112 }
1113 
1114 static UINT save_prop( MSISUMMARYINFO *si, HANDLE handle, UINT row )
1115 {
1116     static const char fmt_systemtime[] = "%04u/%02u/%02u %02u:%02u:%02u";
1117     char data[36]; /* largest string: YYYY/MM/DD hh:mm:ss */
1118     static const char fmt_begin[] = "%u\t";
1119     static const char data_end[] = "\r\n";
1120     static const char fmt_int[] = "%u";
1121     UINT r, data_type;
1122     SYSTEMTIME system_time;
1123     FILETIME file_time;
1124     INT int_value;
1125     awstring str;
1126     DWORD len, sz;
1127 
1128     str.unicode = FALSE;
1129     str.str.a = NULL;
1130     len = 0;
1131     r = get_prop( si, row, &data_type, &int_value, &file_time, &str, &len );
1132     if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA)
1133         return r;
1134     if (data_type == VT_EMPTY)
1135         return ERROR_SUCCESS; /* property not set */
1136     sz = sprintf( data, fmt_begin, row );
1137     if (!WriteFile( handle, data, sz, &sz, NULL ))
1138         return ERROR_WRITE_FAULT;
1139 
1140     switch( data_type )
1141     {
1142     case VT_I2:
1143     case VT_I4:
1144         sz = sprintf( data, fmt_int, int_value );
1145         if (!WriteFile( handle, data, sz, &sz, NULL ))
1146             return ERROR_WRITE_FAULT;
1147         break;
1148     case VT_LPSTR:
1149         len++;
1150         if (!(str.str.a = msi_alloc( len )))
1151             return ERROR_OUTOFMEMORY;
1152         r = get_prop( si, row, NULL, NULL, NULL, &str, &len );
1153         if (r != ERROR_SUCCESS)
1154         {
1155             msi_free( str.str.a );
1156             return r;
1157         }
1158         sz = len;
1159         if (!WriteFile( handle, str.str.a, sz, &sz, NULL ))
1160         {
1161             msi_free( str.str.a );
1162             return ERROR_WRITE_FAULT;
1163         }
1164         msi_free( str.str.a );
1165         break;
1166     case VT_FILETIME:
1167         if (!FileTimeToSystemTime( &file_time, &system_time ))
1168             return ERROR_FUNCTION_FAILED;
1169         sz = sprintf( data, fmt_systemtime, system_time.wYear, system_time.wMonth,
1170                       system_time.wDay, system_time.wHour, system_time.wMinute,
1171                       system_time.wSecond );
1172         if (!WriteFile( handle, data, sz, &sz, NULL ))
1173             return ERROR_WRITE_FAULT;
1174         break;
1175     case VT_EMPTY:
1176         /* cannot reach here, property not set */
1177         break;
1178     default:
1179         FIXME( "Unknown property variant type\n" );
1180         return ERROR_FUNCTION_FAILED;
1181     }
1182 
1183     sz = ARRAY_SIZE(data_end) - 1;
1184     if (!WriteFile( handle, data_end, sz, &sz, NULL ))
1185         return ERROR_WRITE_FAULT;
1186 
1187     return ERROR_SUCCESS;
1188 }
1189 
1190 UINT msi_export_suminfo( MSIDATABASE *db, HANDLE handle )
1191 {
1192     UINT i, r, num_rows;
1193     MSISUMMARYINFO *si;
1194 
1195     r = msi_get_suminfo( db->storage, 0, &si );
1196     if (r != ERROR_SUCCESS)
1197         r = msi_get_db_suminfo( db, 0, &si );
1198     if (r != ERROR_SUCCESS)
1199         return r;
1200 
1201     num_rows = get_property_count( si->property );
1202     if (!num_rows)
1203     {
1204         msiobj_release( &si->hdr );
1205         return ERROR_FUNCTION_FAILED;
1206     }
1207 
1208     for (i = 0; i < num_rows; i++)
1209     {
1210         r = save_prop( si, handle, i );
1211         if (r != ERROR_SUCCESS)
1212         {
1213             msiobj_release( &si->hdr );
1214             return r;
1215         }
1216     }
1217 
1218     msiobj_release( &si->hdr );
1219     return ERROR_SUCCESS;
1220 }
1221 
1222 UINT WINAPI MsiSummaryInfoPersist( MSIHANDLE handle )
1223 {
1224     MSISUMMARYINFO *si;
1225     UINT ret;
1226 
1227     TRACE( "%lu\n", handle );
1228 
1229     si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
1230     if( !si )
1231         return ERROR_INVALID_HANDLE;
1232 
1233     ret = suminfo_persist( si );
1234 
1235     msiobj_release( &si->hdr );
1236     return ret;
1237 }
1238 
1239 UINT WINAPI MsiCreateTransformSummaryInfoA( MSIHANDLE db, MSIHANDLE db_ref, const char *transform, int error,
1240                                             int validation )
1241 {
1242     UINT r;
1243     WCHAR *transformW = NULL;
1244 
1245     TRACE( "%lu, %lu, %s, %d, %d\n", db, db_ref, debugstr_a(transform), error, validation );
1246 
1247     if (transform && !(transformW = strdupAtoW( transform )))
1248         return ERROR_OUTOFMEMORY;
1249 
1250     r = MsiCreateTransformSummaryInfoW( db, db_ref, transformW, error, validation );
1251     msi_free( transformW );
1252     return r;
1253 }
1254 
1255 UINT WINAPI MsiCreateTransformSummaryInfoW( MSIHANDLE db, MSIHANDLE db_ref, const WCHAR *transform, int error,
1256                                             int validation )
1257 {
1258     FIXME( "%lu, %lu, %s, %d, %d\n", db, db_ref, debugstr_w(transform), error, validation );
1259     return ERROR_FUNCTION_FAILED;
1260 }
1261 
1262 UINT msi_load_suminfo_properties( MSIPACKAGE *package )
1263 {
1264     MSISUMMARYINFO *si;
1265     WCHAR *package_code;
1266     UINT r;
1267     DWORD len;
1268     awstring str;
1269     INT count;
1270 
1271     r = msi_get_suminfo( package->db->storage, 0, &si );
1272     if (r != ERROR_SUCCESS)
1273     {
1274         r = msi_get_db_suminfo( package->db, 0, &si );
1275         if (r != ERROR_SUCCESS)
1276         {
1277             ERR("Unable to open summary information stream %u\n", r);
1278             return r;
1279         }
1280     }
1281 
1282     str.unicode = TRUE;
1283     str.str.w = NULL;
1284     len = 0;
1285     r = get_prop( si, PID_REVNUMBER, NULL, NULL, NULL, &str, &len );
1286     if (r != ERROR_MORE_DATA)
1287     {
1288         WARN("Unable to query revision number %u\n", r);
1289         msiobj_release( &si->hdr );
1290         return ERROR_FUNCTION_FAILED;
1291     }
1292 
1293     len++;
1294     if (!(package_code = msi_alloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
1295     str.str.w = package_code;
1296 
1297     r = get_prop( si, PID_REVNUMBER, NULL, NULL, NULL, &str, &len );
1298     if (r != ERROR_SUCCESS)
1299     {
1300         msi_free( package_code );
1301         msiobj_release( &si->hdr );
1302         return r;
1303     }
1304 
1305     r = msi_set_property( package->db, L"PackageCode", package_code, len );
1306     msi_free( package_code );
1307 
1308     count = 0;
1309     get_prop( si, PID_WORDCOUNT, NULL, &count, NULL, NULL, NULL );
1310     package->WordCount = count;
1311 
1312     msiobj_release( &si->hdr );
1313     return r;
1314 }
1315 
1316 UINT __cdecl s_remote_SummaryInfoGetPropertyCount( MSIHANDLE suminfo, UINT *count )
1317 {
1318     return MsiSummaryInfoGetPropertyCount( suminfo, count );
1319 }
1320 
1321 UINT __cdecl s_remote_SummaryInfoGetProperty( MSIHANDLE suminfo, UINT property, UINT *type,
1322                                               INT *value, FILETIME *ft, LPWSTR *buf )
1323 {
1324     WCHAR empty[1];
1325     DWORD size = 0;
1326     UINT r;
1327 
1328     r = MsiSummaryInfoGetPropertyW( suminfo, property, type, value, ft, empty, &size );
1329     if (r == ERROR_MORE_DATA)
1330     {
1331         size++;
1332         *buf = midl_user_allocate( size * sizeof(WCHAR) );
1333         if (!*buf) return ERROR_OUTOFMEMORY;
1334         r = MsiSummaryInfoGetPropertyW( suminfo, property, type, value, ft, *buf, &size );
1335     }
1336     return r;
1337 }
1338