[MSI] Sync with Wine Staging 1.7.55. CORE-10536
[reactos.git] / reactos / dll / win32 / msi / suminfo.c
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 static UINT save_prop( MSISUMMARYINFO *si, HANDLE handle, UINT row )
1004 {
1005 static const char fmt_systemtime[] = "%d/%02d/%02d %02d:%02d:%02d";
1006 char data[20]; /* largest string: YYYY/MM/DD hh:mm:ss */
1007 static const char fmt_begin[] = "%u\t";
1008 static const char data_end[] = "\r\n";
1009 static const char fmt_int[] = "%u";
1010 UINT r, data_type, len;
1011 SYSTEMTIME system_time;
1012 FILETIME file_time;
1013 INT int_value;
1014 awstring str;
1015 DWORD sz;
1016
1017 str.unicode = FALSE;
1018 str.str.a = NULL;
1019 len = 0;
1020 r = get_prop( si, row, &data_type, &int_value, &file_time, &str, &len );
1021 if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA)
1022 return r;
1023 if (data_type == VT_EMPTY)
1024 return ERROR_SUCCESS; /* property not set */
1025 snprintf( data, sizeof(data), fmt_begin, row );
1026 sz = lstrlenA( data );
1027 if (!WriteFile( handle, data, sz, &sz, NULL ))
1028 return ERROR_WRITE_FAULT;
1029
1030 switch (data_type)
1031 {
1032 case VT_I2:
1033 case VT_I4:
1034 snprintf( data, sizeof(data), fmt_int, int_value );
1035 sz = lstrlenA( data );
1036 if (!WriteFile( handle, data, sz, &sz, NULL ))
1037 return ERROR_WRITE_FAULT;
1038 break;
1039 case VT_LPSTR:
1040 len++;
1041 if (!(str.str.a = msi_alloc( len )))
1042 return ERROR_OUTOFMEMORY;
1043 r = get_prop( si, row, NULL, NULL, NULL, &str, &len );
1044 if (r != ERROR_SUCCESS)
1045 {
1046 msi_free( str.str.a );
1047 return r;
1048 }
1049 sz = lstrlenA( str.str.a );
1050 if (!WriteFile( handle, str.str.a, sz, &sz, NULL ))
1051 {
1052 msi_free( str.str.a );
1053 return ERROR_WRITE_FAULT;
1054 }
1055 msi_free( str.str.a );
1056 break;
1057 case VT_FILETIME:
1058 if (!FileTimeToSystemTime( &file_time, &system_time ))
1059 return ERROR_FUNCTION_FAILED;
1060 snprintf( data, sizeof(data), fmt_systemtime, system_time.wYear, system_time.wMonth,
1061 system_time.wDay, system_time.wHour, system_time.wMinute,
1062 system_time.wSecond );
1063 sz = lstrlenA( data );
1064 if (!WriteFile( handle, data, sz, &sz, NULL ))
1065 return ERROR_WRITE_FAULT;
1066 break;
1067 case VT_EMPTY:
1068 /* cannot reach here, property not set */
1069 break;
1070 default:
1071 FIXME( "Unknown property variant type\n" );
1072 return ERROR_FUNCTION_FAILED;
1073 }
1074
1075 sz = lstrlenA( data_end );
1076 if (!WriteFile( handle, data_end, sz, &sz, NULL ))
1077 return ERROR_WRITE_FAULT;
1078
1079 return ERROR_SUCCESS;
1080 }
1081
1082 UINT msi_export_suminfo( MSIDATABASE *db, HANDLE handle )
1083 {
1084 UINT i, r, num_rows;
1085 MSISUMMARYINFO *si;
1086
1087 r = msi_get_suminfo( db->storage, 0, &si );
1088 if (r != ERROR_SUCCESS)
1089 r = msi_get_db_suminfo( db, 0, &si );
1090 if (r != ERROR_SUCCESS)
1091 return r;
1092
1093 num_rows = get_property_count( si->property );
1094 if (!num_rows)
1095 {
1096 msiobj_release( &si->hdr );
1097 return ERROR_FUNCTION_FAILED;
1098 }
1099
1100 for (i = 0; i < num_rows; i++)
1101 {
1102 r = save_prop( si, handle, i );
1103 if (r != ERROR_SUCCESS)
1104 {
1105 msiobj_release( &si->hdr );
1106 return r;
1107 }
1108 }
1109
1110 msiobj_release( &si->hdr );
1111 return ERROR_SUCCESS;
1112 }
1113
1114 UINT WINAPI MsiSummaryInfoPersist( MSIHANDLE handle )
1115 {
1116 MSISUMMARYINFO *si;
1117 UINT ret;
1118
1119 TRACE("%d\n", handle );
1120
1121 si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
1122 if( !si )
1123 return ERROR_INVALID_HANDLE;
1124
1125 ret = suminfo_persist( si );
1126
1127 msiobj_release( &si->hdr );
1128 return ret;
1129 }
1130
1131 UINT WINAPI MsiCreateTransformSummaryInfoA( MSIHANDLE db, MSIHANDLE db_ref, LPCSTR transform, int error, int validation )
1132 {
1133 UINT r;
1134 WCHAR *transformW = NULL;
1135
1136 TRACE("%u, %u, %s, %d, %d\n", db, db_ref, debugstr_a(transform), error, validation);
1137
1138 if (transform && !(transformW = strdupAtoW( transform )))
1139 return ERROR_OUTOFMEMORY;
1140
1141 r = MsiCreateTransformSummaryInfoW( db, db_ref, transformW, error, validation );
1142 msi_free( transformW );
1143 return r;
1144 }
1145
1146 UINT WINAPI MsiCreateTransformSummaryInfoW( MSIHANDLE db, MSIHANDLE db_ref, LPCWSTR transform, int error, int validation )
1147 {
1148 FIXME("%u, %u, %s, %d, %d\n", db, db_ref, debugstr_w(transform), error, validation);
1149 return ERROR_FUNCTION_FAILED;
1150 }
1151
1152 UINT msi_load_suminfo_properties( MSIPACKAGE *package )
1153 {
1154 static const WCHAR packagecodeW[] = {'P','a','c','k','a','g','e','C','o','d','e',0};
1155 MSISUMMARYINFO *si;
1156 WCHAR *package_code;
1157 UINT r, len;
1158 awstring str;
1159 INT count;
1160
1161 r = msi_get_suminfo( package->db->storage, 0, &si );
1162 if (r != ERROR_SUCCESS)
1163 {
1164 r = msi_get_db_suminfo( package->db, 0, &si );
1165 if (r != ERROR_SUCCESS)
1166 {
1167 ERR("Unable to open summary information stream %u\n", r);
1168 return r;
1169 }
1170 }
1171
1172 str.unicode = TRUE;
1173 str.str.w = NULL;
1174 len = 0;
1175 r = get_prop( si, PID_REVNUMBER, NULL, NULL, NULL, &str, &len );
1176 if (r != ERROR_MORE_DATA)
1177 {
1178 WARN("Unable to query revision number %u\n", r);
1179 msiobj_release( &si->hdr );
1180 return ERROR_FUNCTION_FAILED;
1181 }
1182
1183 len++;
1184 if (!(package_code = msi_alloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
1185 str.str.w = package_code;
1186
1187 r = get_prop( si, PID_REVNUMBER, NULL, NULL, NULL, &str, &len );
1188 if (r != ERROR_SUCCESS)
1189 {
1190 msi_free( package_code );
1191 msiobj_release( &si->hdr );
1192 return r;
1193 }
1194
1195 r = msi_set_property( package->db, packagecodeW, package_code, len );
1196 msi_free( package_code );
1197
1198 count = 0;
1199 get_prop( si, PID_WORDCOUNT, NULL, &count, NULL, NULL, NULL );
1200 package->WordCount = count;
1201
1202 msiobj_release( &si->hdr );
1203 return r;
1204 }