[OLE32] Sync with Wine Staging 4.0. CORE-15682
[reactos.git] / dll / win32 / ole32 / stg_prop.c
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
4 *
5 * This file contains the compound file implementation
6 * of the storage interface.
7 *
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
12 * Copyright 2005 Juan Lang
13 *
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 *
28 * TODO:
29 * - I don't honor the maximum property set size.
30 * - Certain bogus files could result in reading past the end of a buffer.
31 * - Mac-generated files won't be read correctly, even if they're little
32 * endian, because I disregard whether the generator was a Mac. This means
33 * strings will probably be munged (as I don't understand Mac scripts.)
34 * - Not all PROPVARIANT types are supported.
35 * - User defined properties are not supported, see comment in
36 * PropertyStorage_ReadFromStream
37 */
38
39 #include "config.h"
40 #include "wine/port.h"
41
42 #include <assert.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47
48 #define COBJMACROS
49 #define NONAMELESSUNION
50
51 #include "windef.h"
52 #include "winbase.h"
53 #include "winnls.h"
54 #include "winuser.h"
55 #include "wine/unicode.h"
56 #include "wine/debug.h"
57 #include "dictionary.h"
58 #include "storage32.h"
59 #include "enumx.h"
60 #include "oleauto.h"
61
62 WINE_DEFAULT_DEBUG_CHANNEL(storage);
63
64 #ifdef _MSC_VER
65 #define __ASM_STDCALL_FUNC(name,args,code)
66 #endif
67
68 static inline StorageImpl *impl_from_IPropertySetStorage( IPropertySetStorage *iface )
69 {
70 return CONTAINING_RECORD(iface, StorageImpl, base.IPropertySetStorage_iface);
71 }
72
73 /* These are documented in MSDN,
74 * but they don't seem to be in any header file.
75 */
76 #define PROPSETHDR_BYTEORDER_MAGIC 0xfffe
77 #define PROPSETHDR_OSVER_KIND_WIN16 0
78 #define PROPSETHDR_OSVER_KIND_MAC 1
79 #define PROPSETHDR_OSVER_KIND_WIN32 2
80
81 #define CP_UNICODE 1200
82
83 #define MAX_VERSION_0_PROP_NAME_LENGTH 256
84
85 #define CFTAG_WINDOWS (-1L)
86 #define CFTAG_MACINTOSH (-2L)
87 #define CFTAG_FMTID (-3L)
88 #define CFTAG_NODATA 0L
89
90 typedef struct tagPROPERTYSETHEADER
91 {
92 WORD wByteOrder; /* always 0xfffe */
93 WORD wFormat; /* can be zero or one */
94 DWORD dwOSVer; /* OS version of originating system */
95 CLSID clsid; /* application CLSID */
96 DWORD reserved; /* always 1 */
97 } PROPERTYSETHEADER;
98
99 typedef struct tagFORMATIDOFFSET
100 {
101 FMTID fmtid;
102 DWORD dwOffset; /* from beginning of stream */
103 } FORMATIDOFFSET;
104
105 typedef struct tagPROPERTYSECTIONHEADER
106 {
107 DWORD cbSection;
108 DWORD cProperties;
109 } PROPERTYSECTIONHEADER;
110
111 typedef struct tagPROPERTYIDOFFSET
112 {
113 DWORD propid;
114 DWORD dwOffset; /* from beginning of section */
115 } PROPERTYIDOFFSET;
116
117 typedef struct tagPropertyStorage_impl PropertyStorage_impl;
118
119 /* Initializes the property storage from the stream (and undoes any uncommitted
120 * changes in the process.) Returns an error if there is an error reading or
121 * if the stream format doesn't match what's expected.
122 */
123 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *);
124
125 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *);
126
127 /* Creates the dictionaries used by the property storage. If successful, all
128 * the dictionaries have been created. If failed, none has been. (This makes
129 * it a bit easier to deal with destroying them.)
130 */
131 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *);
132
133 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *);
134
135 /* Copies from propvar to prop. If propvar's type is VT_LPSTR, copies the
136 * string using PropertyStorage_StringCopy.
137 */
138 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
139 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP);
140
141 /* Copies the string src, which is encoded using code page srcCP, and returns
142 * it in *dst, in the code page specified by targetCP. The returned string is
143 * allocated using CoTaskMemAlloc.
144 * If srcCP is CP_UNICODE, src is in fact an LPCWSTR. Similarly, if targetCP
145 * is CP_UNICODE, the returned string is in fact an LPWSTR.
146 * Returns S_OK on success, something else on failure.
147 */
148 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
149 LCID targetCP);
150
151 static const IPropertyStorageVtbl IPropertyStorage_Vtbl;
152 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl;
153 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl;
154 static HRESULT create_EnumSTATPROPSETSTG(StorageImpl *, IEnumSTATPROPSETSTG**);
155 static HRESULT create_EnumSTATPROPSTG(PropertyStorage_impl *, IEnumSTATPROPSTG**);
156
157 /***********************************************************************
158 * Implementation of IPropertyStorage
159 */
160 struct tagPropertyStorage_impl
161 {
162 IPropertyStorage IPropertyStorage_iface;
163 LONG ref;
164 CRITICAL_SECTION cs;
165 IStream *stm;
166 BOOL dirty;
167 FMTID fmtid;
168 CLSID clsid;
169 WORD format;
170 DWORD originatorOS;
171 DWORD grfFlags;
172 DWORD grfMode;
173 UINT codePage;
174 LCID locale;
175 PROPID highestProp;
176 struct dictionary *name_to_propid;
177 struct dictionary *propid_to_name;
178 struct dictionary *propid_to_prop;
179 };
180
181 static inline PropertyStorage_impl *impl_from_IPropertyStorage(IPropertyStorage *iface)
182 {
183 return CONTAINING_RECORD(iface, PropertyStorage_impl, IPropertyStorage_iface);
184 }
185
186 /************************************************************************
187 * IPropertyStorage_fnQueryInterface (IPropertyStorage)
188 */
189 static HRESULT WINAPI IPropertyStorage_fnQueryInterface(
190 IPropertyStorage *iface,
191 REFIID riid,
192 void** ppvObject)
193 {
194 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
195
196 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
197
198 if (!ppvObject)
199 return E_INVALIDARG;
200
201 *ppvObject = 0;
202
203 if (IsEqualGUID(&IID_IUnknown, riid) ||
204 IsEqualGUID(&IID_IPropertyStorage, riid))
205 {
206 IPropertyStorage_AddRef(iface);
207 *ppvObject = iface;
208 return S_OK;
209 }
210
211 return E_NOINTERFACE;
212 }
213
214 /************************************************************************
215 * IPropertyStorage_fnAddRef (IPropertyStorage)
216 */
217 static ULONG WINAPI IPropertyStorage_fnAddRef(
218 IPropertyStorage *iface)
219 {
220 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
221 return InterlockedIncrement(&This->ref);
222 }
223
224 /************************************************************************
225 * IPropertyStorage_fnRelease (IPropertyStorage)
226 */
227 static ULONG WINAPI IPropertyStorage_fnRelease(
228 IPropertyStorage *iface)
229 {
230 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
231 ULONG ref;
232
233 ref = InterlockedDecrement(&This->ref);
234 if (ref == 0)
235 {
236 TRACE("Destroying %p\n", This);
237 if (This->dirty)
238 IPropertyStorage_Commit(iface, STGC_DEFAULT);
239 IStream_Release(This->stm);
240 This->cs.DebugInfo->Spare[0] = 0;
241 DeleteCriticalSection(&This->cs);
242 PropertyStorage_DestroyDictionaries(This);
243 HeapFree(GetProcessHeap(), 0, This);
244 }
245 return ref;
246 }
247
248 static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
249 DWORD propid)
250 {
251 PROPVARIANT *ret = NULL;
252
253 dictionary_find(This->propid_to_prop, UlongToPtr(propid), (void **)&ret);
254 TRACE("returning %p\n", ret);
255 return ret;
256 }
257
258 /* Returns NULL if name is NULL. */
259 static PROPVARIANT *PropertyStorage_FindPropertyByName(
260 PropertyStorage_impl *This, LPCWSTR name)
261 {
262 PROPVARIANT *ret = NULL;
263 void *propid;
264
265 if (!name)
266 return NULL;
267 if (This->codePage == CP_UNICODE)
268 {
269 if (dictionary_find(This->name_to_propid, name, &propid))
270 ret = PropertyStorage_FindProperty(This, PtrToUlong(propid));
271 }
272 else
273 {
274 LPSTR ansiName;
275 HRESULT hr = PropertyStorage_StringCopy((LPCSTR)name, CP_UNICODE,
276 &ansiName, This->codePage);
277
278 if (SUCCEEDED(hr))
279 {
280 if (dictionary_find(This->name_to_propid, ansiName, &propid))
281 ret = PropertyStorage_FindProperty(This, PtrToUlong(propid));
282 CoTaskMemFree(ansiName);
283 }
284 }
285 TRACE("returning %p\n", ret);
286 return ret;
287 }
288
289 static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
290 DWORD propid)
291 {
292 LPWSTR ret = NULL;
293
294 dictionary_find(This->propid_to_name, UlongToPtr(propid), (void **)&ret);
295 TRACE("returning %p\n", ret);
296 return ret;
297 }
298
299 /************************************************************************
300 * IPropertyStorage_fnReadMultiple (IPropertyStorage)
301 */
302 static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
303 IPropertyStorage* iface,
304 ULONG cpspec,
305 const PROPSPEC rgpspec[],
306 PROPVARIANT rgpropvar[])
307 {
308 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
309 HRESULT hr = S_OK;
310 ULONG i;
311
312 TRACE("(%p, %d, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
313
314 if (!cpspec)
315 return S_FALSE;
316 if (!rgpspec || !rgpropvar)
317 return E_INVALIDARG;
318 EnterCriticalSection(&This->cs);
319 for (i = 0; i < cpspec; i++)
320 {
321 PropVariantInit(&rgpropvar[i]);
322 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
323 {
324 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
325 rgpspec[i].u.lpwstr);
326
327 if (prop)
328 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, GetACP(),
329 This->codePage);
330 }
331 else
332 {
333 switch (rgpspec[i].u.propid)
334 {
335 case PID_CODEPAGE:
336 rgpropvar[i].vt = VT_I2;
337 rgpropvar[i].u.iVal = This->codePage;
338 break;
339 case PID_LOCALE:
340 rgpropvar[i].vt = VT_I4;
341 rgpropvar[i].u.lVal = This->locale;
342 break;
343 default:
344 {
345 PROPVARIANT *prop = PropertyStorage_FindProperty(This,
346 rgpspec[i].u.propid);
347
348 if (prop)
349 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop,
350 GetACP(), This->codePage);
351 else
352 hr = S_FALSE;
353 }
354 }
355 }
356 }
357 LeaveCriticalSection(&This->cs);
358 return hr;
359 }
360
361 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
362 LCID dstCP)
363 {
364 HRESULT hr = S_OK;
365 int len;
366
367 TRACE("%s, %p, %d, %d\n",
368 srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst,
369 dstCP, srcCP);
370 assert(src);
371 assert(dst);
372 *dst = NULL;
373 if (dstCP == srcCP)
374 {
375 size_t len;
376
377 if (dstCP == CP_UNICODE)
378 len = (strlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
379 else
380 len = strlen(src) + 1;
381 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
382 if (!*dst)
383 hr = STG_E_INSUFFICIENTMEMORY;
384 else
385 memcpy(*dst, src, len);
386 }
387 else
388 {
389 if (dstCP == CP_UNICODE)
390 {
391 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
392 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
393 if (!*dst)
394 hr = STG_E_INSUFFICIENTMEMORY;
395 else
396 MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len);
397 }
398 else
399 {
400 LPCWSTR wideStr = NULL;
401 LPWSTR wideStr_tmp = NULL;
402
403 if (srcCP == CP_UNICODE)
404 wideStr = (LPCWSTR)src;
405 else
406 {
407 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
408 wideStr_tmp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
409 if (wideStr_tmp)
410 {
411 MultiByteToWideChar(srcCP, 0, src, -1, wideStr_tmp, len);
412 wideStr = wideStr_tmp;
413 }
414 else
415 hr = STG_E_INSUFFICIENTMEMORY;
416 }
417 if (SUCCEEDED(hr))
418 {
419 len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0,
420 NULL, NULL);
421 *dst = CoTaskMemAlloc(len);
422 if (!*dst)
423 hr = STG_E_INSUFFICIENTMEMORY;
424 else
425 {
426 BOOL defCharUsed = FALSE;
427
428 if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len,
429 NULL, &defCharUsed) == 0 || defCharUsed)
430 {
431 CoTaskMemFree(*dst);
432 *dst = NULL;
433 hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
434 }
435 }
436 }
437 HeapFree(GetProcessHeap(), 0, wideStr_tmp);
438 }
439 }
440 TRACE("returning 0x%08x (%s)\n", hr,
441 dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst));
442 return hr;
443 }
444
445 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
446 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP)
447 {
448 HRESULT hr = S_OK;
449
450 assert(prop);
451 assert(propvar);
452 if (propvar->vt == VT_LPSTR)
453 {
454 hr = PropertyStorage_StringCopy(propvar->u.pszVal, srcCP,
455 &prop->u.pszVal, targetCP);
456 if (SUCCEEDED(hr))
457 prop->vt = VT_LPSTR;
458 }
459 else
460 PropVariantCopy(prop, propvar);
461 return hr;
462 }
463
464 /* Stores the property with id propid and value propvar into this property
465 * storage. lcid is ignored if propvar's type is not VT_LPSTR. If propvar's
466 * type is VT_LPSTR, converts the string using lcid as the source code page
467 * and This->codePage as the target code page before storing.
468 * As a side effect, may change This->format to 1 if the type of propvar is
469 * a version 1-only property.
470 */
471 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
472 PROPID propid, const PROPVARIANT *propvar, LCID lcid)
473 {
474 HRESULT hr = S_OK;
475 PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
476
477 assert(propvar);
478 if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY)
479 This->format = 1;
480 switch (propvar->vt)
481 {
482 case VT_DECIMAL:
483 case VT_I1:
484 case VT_INT:
485 case VT_UINT:
486 case VT_VECTOR|VT_I1:
487 This->format = 1;
488 }
489 TRACE("Setting 0x%08x to type %d\n", propid, propvar->vt);
490 if (prop)
491 {
492 PropVariantClear(prop);
493 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
494 lcid);
495 }
496 else
497 {
498 prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
499 sizeof(PROPVARIANT));
500 if (prop)
501 {
502 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
503 lcid);
504 if (SUCCEEDED(hr))
505 {
506 dictionary_insert(This->propid_to_prop, UlongToPtr(propid), prop);
507 if (propid > This->highestProp)
508 This->highestProp = propid;
509 }
510 else
511 HeapFree(GetProcessHeap(), 0, prop);
512 }
513 else
514 hr = STG_E_INSUFFICIENTMEMORY;
515 }
516 return hr;
517 }
518
519 /* Adds the name srcName to the name dictionaries, mapped to property ID id.
520 * srcName is encoded in code page cp, and is converted to This->codePage.
521 * If cp is CP_UNICODE, srcName is actually a unicode string.
522 * As a side effect, may change This->format to 1 if srcName is too long for
523 * a version 0 property storage.
524 * Doesn't validate id.
525 */
526 static HRESULT PropertyStorage_StoreNameWithId(PropertyStorage_impl *This,
527 LPCSTR srcName, LCID cp, PROPID id)
528 {
529 LPSTR name;
530 HRESULT hr;
531
532 assert(srcName);
533
534 hr = PropertyStorage_StringCopy(srcName, cp, &name, This->codePage);
535 if (SUCCEEDED(hr))
536 {
537 if (This->codePage == CP_UNICODE)
538 {
539 if (lstrlenW((LPWSTR)name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
540 This->format = 1;
541 }
542 else
543 {
544 if (strlen(name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
545 This->format = 1;
546 }
547 TRACE("Adding prop name %s, propid %d\n",
548 This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) :
549 debugstr_a(name), id);
550 dictionary_insert(This->name_to_propid, name, UlongToPtr(id));
551 dictionary_insert(This->propid_to_name, UlongToPtr(id), name);
552 }
553 return hr;
554 }
555
556 /************************************************************************
557 * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
558 */
559 static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
560 IPropertyStorage* iface,
561 ULONG cpspec,
562 const PROPSPEC rgpspec[],
563 const PROPVARIANT rgpropvar[],
564 PROPID propidNameFirst)
565 {
566 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
567 HRESULT hr = S_OK;
568 ULONG i;
569
570 TRACE("(%p, %d, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
571
572 if (cpspec && (!rgpspec || !rgpropvar))
573 return E_INVALIDARG;
574 if (!(This->grfMode & STGM_READWRITE))
575 return STG_E_ACCESSDENIED;
576 EnterCriticalSection(&This->cs);
577 This->dirty = TRUE;
578 This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
579 PROPSETHDR_OSVER_KIND_WIN32) ;
580 for (i = 0; i < cpspec; i++)
581 {
582 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
583 {
584 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
585 rgpspec[i].u.lpwstr);
586
587 if (prop)
588 PropVariantCopy(prop, &rgpropvar[i]);
589 else
590 {
591 /* Note that I don't do the special cases here that I do below,
592 * because naming the special PIDs isn't supported.
593 */
594 if (propidNameFirst < PID_FIRST_USABLE ||
595 propidNameFirst >= PID_MIN_READONLY)
596 hr = STG_E_INVALIDPARAMETER;
597 else
598 {
599 PROPID nextId = max(propidNameFirst, This->highestProp + 1);
600
601 hr = PropertyStorage_StoreNameWithId(This,
602 (LPCSTR)rgpspec[i].u.lpwstr, CP_UNICODE, nextId);
603 if (SUCCEEDED(hr))
604 hr = PropertyStorage_StorePropWithId(This, nextId,
605 &rgpropvar[i], GetACP());
606 }
607 }
608 }
609 else
610 {
611 switch (rgpspec[i].u.propid)
612 {
613 case PID_DICTIONARY:
614 /* Can't set the dictionary */
615 hr = STG_E_INVALIDPARAMETER;
616 break;
617 case PID_CODEPAGE:
618 /* Can only set the code page if nothing else has been set */
619 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
620 rgpropvar[i].vt == VT_I2)
621 {
622 This->codePage = rgpropvar[i].u.iVal;
623 if (This->codePage == CP_UNICODE)
624 This->grfFlags &= ~PROPSETFLAG_ANSI;
625 else
626 This->grfFlags |= PROPSETFLAG_ANSI;
627 }
628 else
629 hr = STG_E_INVALIDPARAMETER;
630 break;
631 case PID_LOCALE:
632 /* Can only set the locale if nothing else has been set */
633 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
634 rgpropvar[i].vt == VT_I4)
635 This->locale = rgpropvar[i].u.lVal;
636 else
637 hr = STG_E_INVALIDPARAMETER;
638 break;
639 case PID_ILLEGAL:
640 /* silently ignore like MSDN says */
641 break;
642 default:
643 if (rgpspec[i].u.propid >= PID_MIN_READONLY)
644 hr = STG_E_INVALIDPARAMETER;
645 else
646 hr = PropertyStorage_StorePropWithId(This,
647 rgpspec[i].u.propid, &rgpropvar[i], GetACP());
648 }
649 }
650 }
651 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
652 IPropertyStorage_Commit(iface, STGC_DEFAULT);
653 LeaveCriticalSection(&This->cs);
654 return hr;
655 }
656
657 /************************************************************************
658 * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
659 */
660 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
661 IPropertyStorage* iface,
662 ULONG cpspec,
663 const PROPSPEC rgpspec[])
664 {
665 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
666 ULONG i;
667 HRESULT hr;
668
669 TRACE("(%p, %d, %p)\n", iface, cpspec, rgpspec);
670
671 if (cpspec && !rgpspec)
672 return E_INVALIDARG;
673 if (!(This->grfMode & STGM_READWRITE))
674 return STG_E_ACCESSDENIED;
675 hr = S_OK;
676 EnterCriticalSection(&This->cs);
677 This->dirty = TRUE;
678 for (i = 0; i < cpspec; i++)
679 {
680 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
681 {
682 void *propid;
683
684 if (dictionary_find(This->name_to_propid, rgpspec[i].u.lpwstr, &propid))
685 dictionary_remove(This->propid_to_prop, propid);
686 }
687 else
688 {
689 if (rgpspec[i].u.propid >= PID_FIRST_USABLE &&
690 rgpspec[i].u.propid < PID_MIN_READONLY)
691 dictionary_remove(This->propid_to_prop, UlongToPtr(rgpspec[i].u.propid));
692 else
693 hr = STG_E_INVALIDPARAMETER;
694 }
695 }
696 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
697 IPropertyStorage_Commit(iface, STGC_DEFAULT);
698 LeaveCriticalSection(&This->cs);
699 return hr;
700 }
701
702 /************************************************************************
703 * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
704 */
705 static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
706 IPropertyStorage* iface,
707 ULONG cpropid,
708 const PROPID rgpropid[],
709 LPOLESTR rglpwstrName[])
710 {
711 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
712 ULONG i;
713 HRESULT hr = S_FALSE;
714
715 TRACE("(%p, %d, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
716
717 if (cpropid && (!rgpropid || !rglpwstrName))
718 return E_INVALIDARG;
719 EnterCriticalSection(&This->cs);
720 for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
721 {
722 LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
723
724 if (name)
725 {
726 size_t len = lstrlenW(name);
727
728 hr = S_OK;
729 rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
730 if (rglpwstrName[i])
731 memcpy(rglpwstrName[i], name, (len + 1) * sizeof(WCHAR));
732 else
733 hr = STG_E_INSUFFICIENTMEMORY;
734 }
735 else
736 rglpwstrName[i] = NULL;
737 }
738 LeaveCriticalSection(&This->cs);
739 return hr;
740 }
741
742 /************************************************************************
743 * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
744 */
745 static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
746 IPropertyStorage* iface,
747 ULONG cpropid,
748 const PROPID rgpropid[],
749 const LPOLESTR rglpwstrName[])
750 {
751 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
752 ULONG i;
753 HRESULT hr;
754
755 TRACE("(%p, %d, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
756
757 if (cpropid && (!rgpropid || !rglpwstrName))
758 return E_INVALIDARG;
759 if (!(This->grfMode & STGM_READWRITE))
760 return STG_E_ACCESSDENIED;
761 hr = S_OK;
762 EnterCriticalSection(&This->cs);
763 This->dirty = TRUE;
764 for (i = 0; SUCCEEDED(hr) && i < cpropid; i++)
765 {
766 if (rgpropid[i] != PID_ILLEGAL)
767 hr = PropertyStorage_StoreNameWithId(This, (LPCSTR)rglpwstrName[i],
768 CP_UNICODE, rgpropid[i]);
769 }
770 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
771 IPropertyStorage_Commit(iface, STGC_DEFAULT);
772 LeaveCriticalSection(&This->cs);
773 return hr;
774 }
775
776 /************************************************************************
777 * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
778 */
779 static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
780 IPropertyStorage* iface,
781 ULONG cpropid,
782 const PROPID rgpropid[])
783 {
784 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
785 ULONG i;
786 HRESULT hr;
787
788 TRACE("(%p, %d, %p)\n", iface, cpropid, rgpropid);
789
790 if (cpropid && !rgpropid)
791 return E_INVALIDARG;
792 if (!(This->grfMode & STGM_READWRITE))
793 return STG_E_ACCESSDENIED;
794 hr = S_OK;
795 EnterCriticalSection(&This->cs);
796 This->dirty = TRUE;
797 for (i = 0; i < cpropid; i++)
798 {
799 LPWSTR name = NULL;
800
801 if (dictionary_find(This->propid_to_name, UlongToPtr(rgpropid[i]), (void **)&name))
802 {
803 dictionary_remove(This->propid_to_name, UlongToPtr(rgpropid[i]));
804 dictionary_remove(This->name_to_propid, name);
805 }
806 }
807 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
808 IPropertyStorage_Commit(iface, STGC_DEFAULT);
809 LeaveCriticalSection(&This->cs);
810 return hr;
811 }
812
813 /************************************************************************
814 * IPropertyStorage_fnCommit (IPropertyStorage)
815 */
816 static HRESULT WINAPI IPropertyStorage_fnCommit(
817 IPropertyStorage* iface,
818 DWORD grfCommitFlags)
819 {
820 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
821 HRESULT hr;
822
823 TRACE("(%p, 0x%08x)\n", iface, grfCommitFlags);
824
825 if (!(This->grfMode & STGM_READWRITE))
826 return STG_E_ACCESSDENIED;
827 EnterCriticalSection(&This->cs);
828 if (This->dirty)
829 hr = PropertyStorage_WriteToStream(This);
830 else
831 hr = S_OK;
832 LeaveCriticalSection(&This->cs);
833 return hr;
834 }
835
836 /************************************************************************
837 * IPropertyStorage_fnRevert (IPropertyStorage)
838 */
839 static HRESULT WINAPI IPropertyStorage_fnRevert(
840 IPropertyStorage* iface)
841 {
842 HRESULT hr;
843 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
844
845 TRACE("%p\n", iface);
846
847 EnterCriticalSection(&This->cs);
848 if (This->dirty)
849 {
850 PropertyStorage_DestroyDictionaries(This);
851 hr = PropertyStorage_CreateDictionaries(This);
852 if (SUCCEEDED(hr))
853 hr = PropertyStorage_ReadFromStream(This);
854 }
855 else
856 hr = S_OK;
857 LeaveCriticalSection(&This->cs);
858 return hr;
859 }
860
861 /************************************************************************
862 * IPropertyStorage_fnEnum (IPropertyStorage)
863 */
864 static HRESULT WINAPI IPropertyStorage_fnEnum(
865 IPropertyStorage* iface,
866 IEnumSTATPROPSTG** ppenum)
867 {
868 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
869 return create_EnumSTATPROPSTG(This, ppenum);
870 }
871
872 /************************************************************************
873 * IPropertyStorage_fnSetTimes (IPropertyStorage)
874 */
875 static HRESULT WINAPI IPropertyStorage_fnSetTimes(
876 IPropertyStorage* iface,
877 const FILETIME* pctime,
878 const FILETIME* patime,
879 const FILETIME* pmtime)
880 {
881 FIXME("\n");
882 return E_NOTIMPL;
883 }
884
885 /************************************************************************
886 * IPropertyStorage_fnSetClass (IPropertyStorage)
887 */
888 static HRESULT WINAPI IPropertyStorage_fnSetClass(
889 IPropertyStorage* iface,
890 REFCLSID clsid)
891 {
892 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
893
894 TRACE("%p, %s\n", iface, debugstr_guid(clsid));
895
896 if (!clsid)
897 return E_INVALIDARG;
898 if (!(This->grfMode & STGM_READWRITE))
899 return STG_E_ACCESSDENIED;
900 This->clsid = *clsid;
901 This->dirty = TRUE;
902 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
903 IPropertyStorage_Commit(iface, STGC_DEFAULT);
904 return S_OK;
905 }
906
907 /************************************************************************
908 * IPropertyStorage_fnStat (IPropertyStorage)
909 */
910 static HRESULT WINAPI IPropertyStorage_fnStat(
911 IPropertyStorage* iface,
912 STATPROPSETSTG* statpsstg)
913 {
914 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
915 STATSTG stat;
916 HRESULT hr;
917
918 TRACE("%p, %p\n", iface, statpsstg);
919
920 if (!statpsstg)
921 return E_INVALIDARG;
922
923 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
924 if (SUCCEEDED(hr))
925 {
926 statpsstg->fmtid = This->fmtid;
927 statpsstg->clsid = This->clsid;
928 statpsstg->grfFlags = This->grfFlags;
929 statpsstg->mtime = stat.mtime;
930 statpsstg->ctime = stat.ctime;
931 statpsstg->atime = stat.atime;
932 statpsstg->dwOSVersion = This->originatorOS;
933 }
934 return hr;
935 }
936
937 static int PropertyStorage_PropNameCompare(const void *a, const void *b,
938 void *extra)
939 {
940 PropertyStorage_impl *This = extra;
941
942 if (This->codePage == CP_UNICODE)
943 {
944 TRACE("(%s, %s)\n", debugstr_w(a), debugstr_w(b));
945 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
946 return lstrcmpW(a, b);
947 else
948 return lstrcmpiW(a, b);
949 }
950 else
951 {
952 TRACE("(%s, %s)\n", debugstr_a(a), debugstr_a(b));
953 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
954 return lstrcmpA(a, b);
955 else
956 return lstrcmpiA(a, b);
957 }
958 }
959
960 static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra)
961 {
962 CoTaskMemFree(k);
963 }
964
965 static int PropertyStorage_PropCompare(const void *a, const void *b,
966 void *extra)
967 {
968 TRACE("(%d, %d)\n", PtrToUlong(a), PtrToUlong(b));
969 return PtrToUlong(a) - PtrToUlong(b);
970 }
971
972 static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra)
973 {
974 PropVariantClear(d);
975 HeapFree(GetProcessHeap(), 0, d);
976 }
977
978 #ifdef WORDS_BIGENDIAN
979 /* Swaps each character in str to or from little endian; assumes the conversion
980 * is symmetric, that is, that lendian16toh is equivalent to htole16.
981 */
982 static void PropertyStorage_ByteSwapString(LPWSTR str, size_t len)
983 {
984 DWORD i;
985
986 /* Swap characters to host order.
987 * FIXME: alignment?
988 */
989 for (i = 0; i < len; i++)
990 str[i] = lendian16toh(str[i]);
991 }
992 #else
993 #define PropertyStorage_ByteSwapString(s, l)
994 #endif
995
996 /* Reads the dictionary from the memory buffer beginning at ptr. Interprets
997 * the entries according to the values of This->codePage and This->locale.
998 * FIXME: there isn't any checking whether the read property extends past the
999 * end of the buffer.
1000 */
1001 static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
1002 BYTE *ptr)
1003 {
1004 DWORD numEntries, i;
1005 HRESULT hr = S_OK;
1006
1007 assert(This->name_to_propid);
1008 assert(This->propid_to_name);
1009
1010 StorageUtl_ReadDWord(ptr, 0, &numEntries);
1011 TRACE("Reading %d entries:\n", numEntries);
1012 ptr += sizeof(DWORD);
1013 for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
1014 {
1015 PROPID propid;
1016 DWORD cbEntry;
1017
1018 StorageUtl_ReadDWord(ptr, 0, &propid);
1019 ptr += sizeof(PROPID);
1020 StorageUtl_ReadDWord(ptr, 0, &cbEntry);
1021 ptr += sizeof(DWORD);
1022 TRACE("Reading entry with ID 0x%08x, %d bytes\n", propid, cbEntry);
1023 /* Make sure the source string is NULL-terminated */
1024 if (This->codePage != CP_UNICODE)
1025 ptr[cbEntry - 1] = '\0';
1026 else
1027 ((LPWSTR)ptr)[cbEntry - 1] = 0;
1028 hr = PropertyStorage_StoreNameWithId(This, (char*)ptr, This->codePage, propid);
1029 if (This->codePage == CP_UNICODE)
1030 {
1031 /* cbEntry is the number of characters */
1032 cbEntry *= 2;
1033
1034 /* Unicode entries are padded to DWORD boundaries */
1035 if (cbEntry % sizeof(DWORD))
1036 ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
1037 }
1038 ptr += cbEntry;
1039 }
1040 return hr;
1041 }
1042
1043 #ifdef __i386__
1044 #define __thiscall_wrapper __stdcall
1045 #else
1046 #define __thiscall_wrapper __cdecl
1047 #endif
1048
1049 static void* __thiscall_wrapper Allocate_CoTaskMemAlloc(void *this, ULONG size)
1050 {
1051 return CoTaskMemAlloc(size);
1052 }
1053
1054 /* FIXME: there isn't any checking whether the read property extends past the
1055 * end of the buffer.
1056 */
1057 static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, const BYTE *data,
1058 UINT codepage, void* (__thiscall_wrapper *allocate)(void *this, ULONG size), void *allocate_data)
1059 {
1060 HRESULT hr = S_OK;
1061 DWORD vt;
1062
1063 assert(prop);
1064 assert(data);
1065 StorageUtl_ReadDWord(data, 0, &vt);
1066 data += sizeof(DWORD);
1067 prop->vt = vt;
1068 switch (prop->vt)
1069 {
1070 case VT_EMPTY:
1071 case VT_NULL:
1072 break;
1073 case VT_I1:
1074 prop->u.cVal = *(const char *)data;
1075 TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
1076 break;
1077 case VT_UI1:
1078 prop->u.bVal = *data;
1079 TRACE("Read byte 0x%x\n", prop->u.bVal);
1080 break;
1081 case VT_BOOL:
1082 StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.boolVal);
1083 TRACE("Read bool %d\n", prop->u.boolVal);
1084 break;
1085 case VT_I2:
1086 StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.iVal);
1087 TRACE("Read short %d\n", prop->u.iVal);
1088 break;
1089 case VT_UI2:
1090 StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
1091 TRACE("Read ushort %d\n", prop->u.uiVal);
1092 break;
1093 case VT_INT:
1094 case VT_I4:
1095 StorageUtl_ReadDWord(data, 0, (DWORD*)&prop->u.lVal);
1096 TRACE("Read long %d\n", prop->u.lVal);
1097 break;
1098 case VT_UINT:
1099 case VT_UI4:
1100 StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
1101 TRACE("Read ulong %d\n", prop->u.ulVal);
1102 break;
1103 case VT_I8:
1104 StorageUtl_ReadULargeInteger(data, 0, (ULARGE_INTEGER *)&prop->u.hVal);
1105 TRACE("Read long long %s\n", wine_dbgstr_longlong(prop->u.hVal.QuadPart));
1106 break;
1107 case VT_UI8:
1108 StorageUtl_ReadULargeInteger(data, 0, &prop->u.uhVal);
1109 TRACE("Read ulong long %s\n", wine_dbgstr_longlong(prop->u.uhVal.QuadPart));
1110 break;
1111 case VT_R8:
1112 memcpy(&prop->u.dblVal, data, sizeof(double));
1113 TRACE("Read double %f\n", prop->u.dblVal);
1114 break;
1115 case VT_LPSTR:
1116 {
1117 DWORD count;
1118
1119 StorageUtl_ReadDWord(data, 0, &count);
1120 if (codepage == CP_UNICODE && count % 2)
1121 {
1122 WARN("Unicode string has odd number of bytes\n");
1123 hr = STG_E_INVALIDHEADER;
1124 }
1125 else
1126 {
1127 prop->u.pszVal = allocate(allocate_data, count);
1128 if (prop->u.pszVal)
1129 {
1130 memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
1131 /* This is stored in the code page specified in codepage.
1132 * Don't convert it, the caller will just store it as-is.
1133 */
1134 if (codepage == CP_UNICODE)
1135 {
1136 /* Make sure it's NULL-terminated */
1137 prop->u.pszVal[count / sizeof(WCHAR) - 1] = '\0';
1138 TRACE("Read string value %s\n",
1139 debugstr_w(prop->u.pwszVal));
1140 }
1141 else
1142 {
1143 /* Make sure it's NULL-terminated */
1144 prop->u.pszVal[count - 1] = '\0';
1145 TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
1146 }
1147 }
1148 else
1149 hr = STG_E_INSUFFICIENTMEMORY;
1150 }
1151 break;
1152 }
1153 case VT_BSTR:
1154 {
1155 DWORD count, wcount;
1156
1157 StorageUtl_ReadDWord(data, 0, &count);
1158 if (codepage == CP_UNICODE && count % 2)
1159 {
1160 WARN("Unicode string has odd number of bytes\n");
1161 hr = STG_E_INVALIDHEADER;
1162 }
1163 else
1164 {
1165 if (codepage == CP_UNICODE)
1166 wcount = count / 2;
1167 else
1168 wcount = MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, NULL, 0);
1169
1170 prop->u.bstrVal = SysAllocStringLen(NULL, wcount); /* FIXME: use allocator? */
1171
1172 if (prop->u.bstrVal)
1173 {
1174 if (codepage == CP_UNICODE)
1175 memcpy(prop->u.bstrVal, data + sizeof(DWORD), count);
1176 else
1177 MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, prop->u.bstrVal, wcount);
1178
1179 prop->u.bstrVal[wcount - 1] = '\0';
1180 TRACE("Read string value %s\n", debugstr_w(prop->u.bstrVal));
1181 }
1182 else
1183 hr = STG_E_INSUFFICIENTMEMORY;
1184 }
1185 break;
1186 }
1187 case VT_BLOB:
1188 {
1189 DWORD count;
1190
1191 StorageUtl_ReadDWord(data, 0, &count);
1192 prop->u.blob.cbSize = count;
1193 prop->u.blob.pBlobData = allocate(allocate_data, count);
1194 if (prop->u.blob.pBlobData)
1195 {
1196 memcpy(prop->u.blob.pBlobData, data + sizeof(DWORD), count);
1197 TRACE("Read blob value of size %d\n", count);
1198 }
1199 else
1200 hr = STG_E_INSUFFICIENTMEMORY;
1201 break;
1202 }
1203 case VT_LPWSTR:
1204 {
1205 DWORD count;
1206
1207 StorageUtl_ReadDWord(data, 0, &count);
1208 prop->u.pwszVal = allocate(allocate_data, count * sizeof(WCHAR));
1209 if (prop->u.pwszVal)
1210 {
1211 memcpy(prop->u.pwszVal, data + sizeof(DWORD),
1212 count * sizeof(WCHAR));
1213 /* make sure string is NULL-terminated */
1214 prop->u.pwszVal[count - 1] = '\0';
1215 PropertyStorage_ByteSwapString(prop->u.pwszVal, count);
1216 TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
1217 }
1218 else
1219 hr = STG_E_INSUFFICIENTMEMORY;
1220 break;
1221 }
1222 case VT_FILETIME:
1223 StorageUtl_ReadULargeInteger(data, 0,
1224 (ULARGE_INTEGER *)&prop->u.filetime);
1225 break;
1226 case VT_CF:
1227 {
1228 DWORD len = 0, tag = 0;
1229
1230 StorageUtl_ReadDWord(data, 0, &len);
1231 StorageUtl_ReadDWord(data, 4, &tag);
1232 if (len > 8)
1233 {
1234 len -= 8;
1235 prop->u.pclipdata = allocate(allocate_data, sizeof (CLIPDATA));
1236 prop->u.pclipdata->cbSize = len;
1237 prop->u.pclipdata->ulClipFmt = tag;
1238 prop->u.pclipdata->pClipData = allocate(allocate_data, len - sizeof(prop->u.pclipdata->ulClipFmt));
1239 memcpy(prop->u.pclipdata->pClipData, data+8, len - sizeof(prop->u.pclipdata->ulClipFmt));
1240 }
1241 else
1242 hr = STG_E_INVALIDPARAMETER;
1243 }
1244 break;
1245 default:
1246 FIXME("unsupported type %d\n", prop->vt);
1247 hr = STG_E_INVALIDPARAMETER;
1248 }
1249 return hr;
1250 }
1251
1252 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
1253 PROPERTYSETHEADER *hdr)
1254 {
1255 BYTE buf[sizeof(PROPERTYSETHEADER)];
1256 ULONG count = 0;
1257 HRESULT hr;
1258
1259 assert(stm);
1260 assert(hdr);
1261 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1262 if (SUCCEEDED(hr))
1263 {
1264 if (count != sizeof(buf))
1265 {
1266 WARN("read only %d\n", count);
1267 hr = STG_E_INVALIDHEADER;
1268 }
1269 else
1270 {
1271 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
1272 &hdr->wByteOrder);
1273 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
1274 &hdr->wFormat);
1275 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
1276 &hdr->dwOSVer);
1277 StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
1278 &hdr->clsid);
1279 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
1280 &hdr->reserved);
1281 }
1282 }
1283 TRACE("returning 0x%08x\n", hr);
1284 return hr;
1285 }
1286
1287 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
1288 FORMATIDOFFSET *fmt)
1289 {
1290 BYTE buf[sizeof(FORMATIDOFFSET)];
1291 ULONG count = 0;
1292 HRESULT hr;
1293
1294 assert(stm);
1295 assert(fmt);
1296 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1297 if (SUCCEEDED(hr))
1298 {
1299 if (count != sizeof(buf))
1300 {
1301 WARN("read only %d\n", count);
1302 hr = STG_E_INVALIDHEADER;
1303 }
1304 else
1305 {
1306 StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
1307 &fmt->fmtid);
1308 StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
1309 &fmt->dwOffset);
1310 }
1311 }
1312 TRACE("returning 0x%08x\n", hr);
1313 return hr;
1314 }
1315
1316 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
1317 PROPERTYSECTIONHEADER *hdr)
1318 {
1319 BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
1320 ULONG count = 0;
1321 HRESULT hr;
1322
1323 assert(stm);
1324 assert(hdr);
1325 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1326 if (SUCCEEDED(hr))
1327 {
1328 if (count != sizeof(buf))
1329 {
1330 WARN("read only %d\n", count);
1331 hr = STG_E_INVALIDHEADER;
1332 }
1333 else
1334 {
1335 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1336 cbSection), &hdr->cbSection);
1337 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1338 cProperties), &hdr->cProperties);
1339 }
1340 }
1341 TRACE("returning 0x%08x\n", hr);
1342 return hr;
1343 }
1344
1345 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
1346 {
1347 PROPERTYSETHEADER hdr;
1348 FORMATIDOFFSET fmtOffset;
1349 PROPERTYSECTIONHEADER sectionHdr;
1350 LARGE_INTEGER seek;
1351 ULONG i;
1352 STATSTG stat;
1353 HRESULT hr;
1354 BYTE *buf = NULL;
1355 ULONG count = 0;
1356 DWORD dictOffset = 0;
1357
1358 This->dirty = FALSE;
1359 This->highestProp = 0;
1360 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1361 if (FAILED(hr))
1362 goto end;
1363 if (stat.cbSize.u.HighPart)
1364 {
1365 WARN("stream too big\n");
1366 /* maximum size varies, but it can't be this big */
1367 hr = STG_E_INVALIDHEADER;
1368 goto end;
1369 }
1370 if (stat.cbSize.u.LowPart == 0)
1371 {
1372 /* empty stream is okay */
1373 hr = S_OK;
1374 goto end;
1375 }
1376 else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
1377 sizeof(FORMATIDOFFSET))
1378 {
1379 WARN("stream too small\n");
1380 hr = STG_E_INVALIDHEADER;
1381 goto end;
1382 }
1383 seek.QuadPart = 0;
1384 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1385 if (FAILED(hr))
1386 goto end;
1387 hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
1388 /* I've only seen reserved == 1, but the article says I shouldn't disallow
1389 * higher values.
1390 */
1391 if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
1392 {
1393 WARN("bad magic in prop set header\n");
1394 hr = STG_E_INVALIDHEADER;
1395 goto end;
1396 }
1397 if (hdr.wFormat != 0 && hdr.wFormat != 1)
1398 {
1399 WARN("bad format version %d\n", hdr.wFormat);
1400 hr = STG_E_INVALIDHEADER;
1401 goto end;
1402 }
1403 This->format = hdr.wFormat;
1404 This->clsid = hdr.clsid;
1405 This->originatorOS = hdr.dwOSVer;
1406 if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1407 WARN("File comes from a Mac, strings will probably be screwed up\n");
1408 hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1409 if (FAILED(hr))
1410 goto end;
1411 if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
1412 {
1413 WARN("invalid offset %d (stream length is %d)\n", fmtOffset.dwOffset,
1414 stat.cbSize.u.LowPart);
1415 hr = STG_E_INVALIDHEADER;
1416 goto end;
1417 }
1418 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1419 * follows not one, but two sections. The first contains the standard properties
1420 * for the document summary information, and the second consists of user-defined
1421 * properties. This is the only case in which multiple sections are
1422 * allowed.
1423 * Reading the second stream isn't implemented yet.
1424 */
1425 hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, &sectionHdr);
1426 if (FAILED(hr))
1427 goto end;
1428 /* The section size includes the section header, so check it */
1429 if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1430 {
1431 WARN("section header too small, got %d\n", sectionHdr.cbSection);
1432 hr = STG_E_INVALIDHEADER;
1433 goto end;
1434 }
1435 buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1436 sizeof(PROPERTYSECTIONHEADER));
1437 if (!buf)
1438 {
1439 hr = STG_E_INSUFFICIENTMEMORY;
1440 goto end;
1441 }
1442 hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
1443 sizeof(PROPERTYSECTIONHEADER), &count);
1444 if (FAILED(hr))
1445 goto end;
1446 TRACE("Reading %d properties:\n", sectionHdr.cProperties);
1447 for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1448 {
1449 PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
1450 i * sizeof(PROPERTYIDOFFSET));
1451
1452 if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1453 idOffset->dwOffset > sectionHdr.cbSection - sizeof(DWORD))
1454 hr = STG_E_INVALIDPOINTER;
1455 else
1456 {
1457 if (idOffset->propid >= PID_FIRST_USABLE &&
1458 idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1459 This->highestProp)
1460 This->highestProp = idOffset->propid;
1461 if (idOffset->propid == PID_DICTIONARY)
1462 {
1463 /* Don't read the dictionary yet, its entries depend on the
1464 * code page. Just store the offset so we know to read it
1465 * later.
1466 */
1467 dictOffset = idOffset->dwOffset;
1468 TRACE("Dictionary offset is %d\n", dictOffset);
1469 }
1470 else
1471 {
1472 PROPVARIANT prop;
1473
1474 PropVariantInit(&prop);
1475 if (SUCCEEDED(PropertyStorage_ReadProperty(&prop,
1476 buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER),
1477 This->codePage, Allocate_CoTaskMemAlloc, NULL)))
1478 {
1479 TRACE("Read property with ID 0x%08x, type %d\n",
1480 idOffset->propid, prop.vt);
1481 switch(idOffset->propid)
1482 {
1483 case PID_CODEPAGE:
1484 if (prop.vt == VT_I2)
1485 This->codePage = (UINT)prop.u.iVal;
1486 break;
1487 case PID_LOCALE:
1488 if (prop.vt == VT_I4)
1489 This->locale = (LCID)prop.u.lVal;
1490 break;
1491 case PID_BEHAVIOR:
1492 if (prop.vt == VT_I4 && prop.u.lVal)
1493 This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1494 /* The format should already be 1, but just in case */
1495 This->format = 1;
1496 break;
1497 default:
1498 hr = PropertyStorage_StorePropWithId(This,
1499 idOffset->propid, &prop, This->codePage);
1500 }
1501 }
1502 PropVariantClear(&prop);
1503 }
1504 }
1505 }
1506 if (!This->codePage)
1507 {
1508 /* default to Unicode unless told not to, as specified on msdn */
1509 if (This->grfFlags & PROPSETFLAG_ANSI)
1510 This->codePage = GetACP();
1511 else
1512 This->codePage = CP_UNICODE;
1513 }
1514 if (!This->locale)
1515 This->locale = LOCALE_SYSTEM_DEFAULT;
1516 TRACE("Code page is %d, locale is %d\n", This->codePage, This->locale);
1517 if (dictOffset)
1518 hr = PropertyStorage_ReadDictionary(This,
1519 buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
1520
1521 end:
1522 HeapFree(GetProcessHeap(), 0, buf);
1523 if (FAILED(hr))
1524 {
1525 dictionary_destroy(This->name_to_propid);
1526 This->name_to_propid = NULL;
1527 dictionary_destroy(This->propid_to_name);
1528 This->propid_to_name = NULL;
1529 dictionary_destroy(This->propid_to_prop);
1530 This->propid_to_prop = NULL;
1531 }
1532 return hr;
1533 }
1534
1535 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
1536 PROPERTYSETHEADER *hdr)
1537 {
1538 assert(hdr);
1539 StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0,
1540 PROPSETHDR_BYTEORDER_MAGIC);
1541 StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, This->format);
1542 StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS);
1543 StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid);
1544 StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1);
1545 }
1546
1547 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
1548 FORMATIDOFFSET *fmtOffset)
1549 {
1550 assert(fmtOffset);
1551 StorageUtl_WriteGUID((BYTE *)fmtOffset, 0, &This->fmtid);
1552 StorageUtl_WriteDWord((BYTE *)fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
1553 sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
1554 }
1555
1556 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
1557 PROPERTYSECTIONHEADER *hdr)
1558 {
1559 assert(hdr);
1560 StorageUtl_WriteDWord((BYTE *)hdr, 0, cbSection);
1561 StorageUtl_WriteDWord((BYTE *)hdr,
1562 offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
1563 }
1564
1565 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
1566 PROPERTYIDOFFSET *propIdOffset)
1567 {
1568 assert(propIdOffset);
1569 StorageUtl_WriteDWord((BYTE *)propIdOffset, 0, propid);
1570 StorageUtl_WriteDWord((BYTE *)propIdOffset,
1571 offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
1572 }
1573
1574 static inline HRESULT PropertStorage_WriteWStringToStream(IStream *stm,
1575 LPCWSTR str, DWORD len, DWORD *written)
1576 {
1577 #ifdef WORDS_BIGENDIAN
1578 WCHAR *leStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1579 HRESULT hr;
1580
1581 if (!leStr)
1582 return E_OUTOFMEMORY;
1583 memcpy(leStr, str, len * sizeof(WCHAR));
1584 PropertyStorage_ByteSwapString(leStr, len);
1585 hr = IStream_Write(stm, leStr, len, written);
1586 HeapFree(GetProcessHeap(), 0, leStr);
1587 return hr;
1588 #else
1589 return IStream_Write(stm, str, len, written);
1590 #endif
1591 }
1592
1593 struct DictionaryClosure
1594 {
1595 HRESULT hr;
1596 DWORD bytesWritten;
1597 };
1598
1599 static BOOL PropertyStorage_DictionaryWriter(const void *key,
1600 const void *value, void *extra, void *closure)
1601 {
1602 PropertyStorage_impl *This = extra;
1603 struct DictionaryClosure *c = closure;
1604 DWORD propid;
1605 ULONG count;
1606
1607 assert(key);
1608 assert(closure);
1609 StorageUtl_WriteDWord((LPBYTE)&propid, 0, PtrToUlong(value));
1610 c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
1611 if (FAILED(c->hr))
1612 goto end;
1613 c->bytesWritten += sizeof(DWORD);
1614 if (This->codePage == CP_UNICODE)
1615 {
1616 DWORD keyLen, pad = 0;
1617
1618 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0,
1619 (lstrlenW((LPCWSTR)key) + 1) * sizeof(WCHAR));
1620 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1621 if (FAILED(c->hr))
1622 goto end;
1623 c->bytesWritten += sizeof(DWORD);
1624 c->hr = PropertStorage_WriteWStringToStream(This->stm, key, keyLen,
1625 &count);
1626 if (FAILED(c->hr))
1627 goto end;
1628 c->bytesWritten += keyLen * sizeof(WCHAR);
1629 if (keyLen % sizeof(DWORD))
1630 {
1631 c->hr = IStream_Write(This->stm, &pad,
1632 sizeof(DWORD) - keyLen % sizeof(DWORD), &count);
1633 if (FAILED(c->hr))
1634 goto end;
1635 c->bytesWritten += sizeof(DWORD) - keyLen % sizeof(DWORD);
1636 }
1637 }
1638 else
1639 {
1640 DWORD keyLen;
1641
1642 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0, strlen((LPCSTR)key) + 1);
1643 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1644 if (FAILED(c->hr))
1645 goto end;
1646 c->bytesWritten += sizeof(DWORD);
1647 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1648 if (FAILED(c->hr))
1649 goto end;
1650 c->bytesWritten += keyLen;
1651 }
1652 end:
1653 return SUCCEEDED(c->hr);
1654 }
1655
1656 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
1657
1658 /* Writes the dictionary to the stream. Assumes without checking that the
1659 * dictionary isn't empty.
1660 */
1661 static HRESULT PropertyStorage_WriteDictionaryToStream(
1662 PropertyStorage_impl *This, DWORD *sectionOffset)
1663 {
1664 HRESULT hr;
1665 LARGE_INTEGER seek;
1666 PROPERTYIDOFFSET propIdOffset;
1667 ULONG count;
1668 DWORD dwTemp;
1669 struct DictionaryClosure closure;
1670
1671 assert(sectionOffset);
1672
1673 /* The dictionary's always the first property written, so seek to its
1674 * spot.
1675 */
1676 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
1677 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1678 if (FAILED(hr))
1679 goto end;
1680 PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
1681 &propIdOffset);
1682 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1683 if (FAILED(hr))
1684 goto end;
1685
1686 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1687 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1688 if (FAILED(hr))
1689 goto end;
1690 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0,
1691 dictionary_num_entries(This->name_to_propid));
1692 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1693 if (FAILED(hr))
1694 goto end;
1695 *sectionOffset += sizeof(dwTemp);
1696
1697 closure.hr = S_OK;
1698 closure.bytesWritten = 0;
1699 dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
1700 &closure);
1701 hr = closure.hr;
1702 if (FAILED(hr))
1703 goto end;
1704 *sectionOffset += closure.bytesWritten;
1705 if (closure.bytesWritten % sizeof(DWORD))
1706 {
1707 DWORD padding = sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
1708 TRACE("adding %d bytes of padding\n", padding);
1709 *sectionOffset += padding;
1710 }
1711
1712 end:
1713 return hr;
1714 }
1715
1716 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
1717 DWORD propNum, DWORD propid, const PROPVARIANT *var, DWORD *sectionOffset)
1718 {
1719 HRESULT hr;
1720 LARGE_INTEGER seek;
1721 PROPERTYIDOFFSET propIdOffset;
1722 ULONG count;
1723 DWORD dwType, bytesWritten;
1724
1725 assert(var);
1726 assert(sectionOffset);
1727
1728 TRACE("%p, %d, 0x%08x, (%d), (%d)\n", This, propNum, propid, var->vt,
1729 *sectionOffset);
1730
1731 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
1732 propNum * sizeof(PROPERTYIDOFFSET);
1733 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1734 if (FAILED(hr))
1735 goto end;
1736 PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
1737 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1738 if (FAILED(hr))
1739 goto end;
1740
1741 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1742 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1743 if (FAILED(hr))
1744 goto end;
1745 StorageUtl_WriteDWord((LPBYTE)&dwType, 0, var->vt);
1746 hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
1747 if (FAILED(hr))
1748 goto end;
1749 *sectionOffset += sizeof(dwType);
1750
1751 switch (var->vt)
1752 {
1753 case VT_EMPTY:
1754 case VT_NULL:
1755 bytesWritten = 0;
1756 break;
1757 case VT_I1:
1758 case VT_UI1:
1759 hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
1760 &count);
1761 bytesWritten = count;
1762 break;
1763 case VT_I2:
1764 case VT_UI2:
1765 {
1766 WORD wTemp;
1767
1768 StorageUtl_WriteWord((LPBYTE)&wTemp, 0, var->u.iVal);
1769 hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
1770 bytesWritten = count;
1771 break;
1772 }
1773 case VT_I4:
1774 case VT_UI4:
1775 {
1776 DWORD dwTemp;
1777
1778 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, var->u.lVal);
1779 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1780 bytesWritten = count;
1781 break;
1782 }
1783 case VT_LPSTR:
1784 {
1785 DWORD len, dwTemp;
1786
1787 if (This->codePage == CP_UNICODE)
1788 len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
1789 else
1790 len = lstrlenA(var->u.pszVal) + 1;
1791 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1792 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1793 if (FAILED(hr))
1794 goto end;
1795 hr = IStream_Write(This->stm, var->u.pszVal, len, &count);
1796 bytesWritten = count + sizeof(DWORD);
1797 break;
1798 }
1799 case VT_LPWSTR:
1800 {
1801 DWORD len = lstrlenW(var->u.pwszVal) + 1, dwTemp;
1802
1803 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1804 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1805 if (FAILED(hr))
1806 goto end;
1807 hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
1808 &count);
1809 bytesWritten = count + sizeof(DWORD);
1810 break;
1811 }
1812 case VT_FILETIME:
1813 {
1814 FILETIME temp;
1815
1816 StorageUtl_WriteULargeInteger((BYTE *)&temp, 0,
1817 (const ULARGE_INTEGER *)&var->u.filetime);
1818 hr = IStream_Write(This->stm, &temp, sizeof(FILETIME), &count);
1819 bytesWritten = count;
1820 break;
1821 }
1822 case VT_CF:
1823 {
1824 DWORD cf_hdr[2], len;
1825
1826 len = var->u.pclipdata->cbSize;
1827 StorageUtl_WriteDWord((LPBYTE)&cf_hdr[0], 0, len + 8);
1828 StorageUtl_WriteDWord((LPBYTE)&cf_hdr[1], 0, var->u.pclipdata->ulClipFmt);
1829 hr = IStream_Write(This->stm, cf_hdr, sizeof(cf_hdr), &count);
1830 if (FAILED(hr))
1831 goto end;
1832 hr = IStream_Write(This->stm, var->u.pclipdata->pClipData,
1833 len - sizeof(var->u.pclipdata->ulClipFmt), &count);
1834 if (FAILED(hr))
1835 goto end;
1836 bytesWritten = count + sizeof cf_hdr;
1837 break;
1838 }
1839 case VT_CLSID:
1840 {
1841 CLSID temp;
1842
1843 StorageUtl_WriteGUID((BYTE *)&temp, 0, var->u.puuid);
1844 hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
1845 bytesWritten = count;
1846 break;
1847 }
1848 default:
1849 FIXME("unsupported type: %d\n", var->vt);
1850 return STG_E_INVALIDPARAMETER;
1851 }
1852
1853 if (SUCCEEDED(hr))
1854 {
1855 *sectionOffset += bytesWritten;
1856 if (bytesWritten % sizeof(DWORD))
1857 {
1858 DWORD padding = sizeof(DWORD) - bytesWritten % sizeof(DWORD);
1859 TRACE("adding %d bytes of padding\n", padding);
1860 *sectionOffset += padding;
1861 }
1862 }
1863
1864 end:
1865 return hr;
1866 }
1867
1868 struct PropertyClosure
1869 {
1870 HRESULT hr;
1871 DWORD propNum;
1872 DWORD *sectionOffset;
1873 };
1874
1875 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
1876 void *extra, void *closure)
1877 {
1878 PropertyStorage_impl *This = extra;
1879 struct PropertyClosure *c = closure;
1880
1881 assert(key);
1882 assert(value);
1883 assert(extra);
1884 assert(closure);
1885 c->hr = PropertyStorage_WritePropertyToStream(This, c->propNum++,
1886 PtrToUlong(key), value, c->sectionOffset);
1887 return SUCCEEDED(c->hr);
1888 }
1889
1890 static HRESULT PropertyStorage_WritePropertiesToStream(
1891 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
1892 {
1893 struct PropertyClosure closure;
1894
1895 assert(sectionOffset);
1896 closure.hr = S_OK;
1897 closure.propNum = startingPropNum;
1898 closure.sectionOffset = sectionOffset;
1899 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
1900 &closure);
1901 return closure.hr;
1902 }
1903
1904 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
1905 {
1906 HRESULT hr;
1907 ULONG count = 0;
1908 LARGE_INTEGER seek = { {0} };
1909 PROPERTYSETHEADER hdr;
1910 FORMATIDOFFSET fmtOffset;
1911
1912 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1913 if (FAILED(hr))
1914 goto end;
1915 PropertyStorage_MakeHeader(This, &hdr);
1916 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
1917 if (FAILED(hr))
1918 goto end;
1919 if (count != sizeof(hdr))
1920 {
1921 hr = STG_E_WRITEFAULT;
1922 goto end;
1923 }
1924
1925 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
1926 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
1927 if (FAILED(hr))
1928 goto end;
1929 if (count != sizeof(fmtOffset))
1930 {
1931 hr = STG_E_WRITEFAULT;
1932 goto end;
1933 }
1934 hr = S_OK;
1935
1936 end:
1937 return hr;
1938 }
1939
1940 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
1941 {
1942 PROPERTYSECTIONHEADER sectionHdr;
1943 HRESULT hr;
1944 ULONG count;
1945 LARGE_INTEGER seek;
1946 DWORD numProps, prop, sectionOffset, dwTemp;
1947 PROPVARIANT var;
1948
1949 PropertyStorage_WriteHeadersToStream(This);
1950
1951 /* Count properties. Always at least one property, the code page */
1952 numProps = 1;
1953 if (dictionary_num_entries(This->name_to_propid))
1954 numProps++;
1955 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1956 numProps++;
1957 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1958 numProps++;
1959 numProps += dictionary_num_entries(This->propid_to_prop);
1960
1961 /* Write section header with 0 bytes right now, I'll adjust it after
1962 * writing properties.
1963 */
1964 PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
1965 seek.QuadPart = SECTIONHEADER_OFFSET;
1966 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1967 if (FAILED(hr))
1968 goto end;
1969 hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
1970 if (FAILED(hr))
1971 goto end;
1972
1973 prop = 0;
1974 sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
1975 numProps * sizeof(PROPERTYIDOFFSET);
1976
1977 if (dictionary_num_entries(This->name_to_propid))
1978 {
1979 prop++;
1980 hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
1981 if (FAILED(hr))
1982 goto end;
1983 }
1984
1985 PropVariantInit(&var);
1986
1987 var.vt = VT_I2;
1988 var.u.iVal = This->codePage;
1989 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
1990 &var, &sectionOffset);
1991 if (FAILED(hr))
1992 goto end;
1993
1994 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1995 {
1996 var.vt = VT_I4;
1997 var.u.lVal = This->locale;
1998 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
1999 &var, &sectionOffset);
2000 if (FAILED(hr))
2001 goto end;
2002 }
2003
2004 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2005 {
2006 var.vt = VT_I4;
2007 var.u.lVal = 1;
2008 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
2009 &var, &sectionOffset);
2010 if (FAILED(hr))
2011 goto end;
2012 }
2013
2014 hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
2015 if (FAILED(hr))
2016 goto end;
2017
2018 /* Now write the byte count of the section */
2019 seek.QuadPart = SECTIONHEADER_OFFSET;
2020 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2021 if (FAILED(hr))
2022 goto end;
2023 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, sectionOffset);
2024 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2025
2026 end:
2027 return hr;
2028 }
2029
2030 /***********************************************************************
2031 * PropertyStorage_Construct
2032 */
2033 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
2034 {
2035 dictionary_destroy(This->name_to_propid);
2036 This->name_to_propid = NULL;
2037 dictionary_destroy(This->propid_to_name);
2038 This->propid_to_name = NULL;
2039 dictionary_destroy(This->propid_to_prop);
2040 This->propid_to_prop = NULL;
2041 }
2042
2043 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
2044 {
2045 HRESULT hr = S_OK;
2046
2047 This->name_to_propid = dictionary_create(
2048 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
2049 This);
2050 if (!This->name_to_propid)
2051 {
2052 hr = STG_E_INSUFFICIENTMEMORY;
2053 goto end;
2054 }
2055 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
2056 NULL, This);
2057 if (!This->propid_to_name)
2058 {
2059 hr = STG_E_INSUFFICIENTMEMORY;
2060 goto end;
2061 }
2062 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
2063 PropertyStorage_PropertyDestroy, This);
2064 if (!This->propid_to_prop)
2065 {
2066 hr = STG_E_INSUFFICIENTMEMORY;
2067 goto end;
2068 }
2069 end:
2070 if (FAILED(hr))
2071 PropertyStorage_DestroyDictionaries(This);
2072 return hr;
2073 }
2074
2075 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
2076 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
2077 {
2078 HRESULT hr = S_OK;
2079
2080 assert(pps);
2081 assert(rfmtid);
2082 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
2083 if (!*pps)
2084 return E_OUTOFMEMORY;
2085
2086 (*pps)->IPropertyStorage_iface.lpVtbl = &IPropertyStorage_Vtbl;
2087 (*pps)->ref = 1;
2088 InitializeCriticalSection(&(*pps)->cs);
2089 (*pps)->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStorage_impl.cs");
2090 (*pps)->stm = stm;
2091 (*pps)->fmtid = *rfmtid;
2092 (*pps)->grfMode = grfMode;
2093
2094 hr = PropertyStorage_CreateDictionaries(*pps);
2095 if (FAILED(hr))
2096 {
2097 (*pps)->cs.DebugInfo->Spare[0] = 0;
2098 DeleteCriticalSection(&(*pps)->cs);
2099 HeapFree(GetProcessHeap(), 0, *pps);
2100 *pps = NULL;
2101 }
2102 else IStream_AddRef( stm );
2103
2104 return hr;
2105 }
2106
2107 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
2108 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
2109 {
2110 PropertyStorage_impl *ps;
2111 HRESULT hr;
2112
2113 assert(pps);
2114 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2115 if (SUCCEEDED(hr))
2116 {
2117 hr = PropertyStorage_ReadFromStream(ps);
2118 if (SUCCEEDED(hr))
2119 {
2120 *pps = &ps->IPropertyStorage_iface;
2121 TRACE("PropertyStorage %p constructed\n", ps);
2122 hr = S_OK;
2123 }
2124 else IPropertyStorage_Release( &ps->IPropertyStorage_iface );
2125 }
2126 return hr;
2127 }
2128
2129 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
2130 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2131 {
2132 PropertyStorage_impl *ps;
2133 HRESULT hr;
2134
2135 assert(pps);
2136 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2137 if (SUCCEEDED(hr))
2138 {
2139 ps->format = 0;
2140 ps->grfFlags = grfFlags;
2141 if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2142 ps->format = 1;
2143 /* default to Unicode unless told not to, as specified on msdn */
2144 if (ps->grfFlags & PROPSETFLAG_ANSI)
2145 ps->codePage = GetACP();
2146 else
2147 ps->codePage = CP_UNICODE;
2148 ps->locale = LOCALE_SYSTEM_DEFAULT;
2149 TRACE("Code page is %d, locale is %d\n", ps->codePage, ps->locale);
2150 *pps = &ps->IPropertyStorage_iface;
2151 TRACE("PropertyStorage %p constructed\n", ps);
2152 hr = S_OK;
2153 }
2154 return hr;
2155 }
2156
2157
2158 /***********************************************************************
2159 * Implementation of IPropertySetStorage
2160 */
2161
2162 /************************************************************************
2163 * IPropertySetStorage_fnQueryInterface (IUnknown)
2164 *
2165 * This method forwards to the common QueryInterface implementation
2166 */
2167 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2168 IPropertySetStorage *ppstg,
2169 REFIID riid,
2170 void** ppvObject)
2171 {
2172 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2173 return IStorage_QueryInterface( &This->base.IStorage_iface, riid, ppvObject );
2174 }
2175
2176 /************************************************************************
2177 * IPropertySetStorage_fnAddRef (IUnknown)
2178 *
2179 * This method forwards to the common AddRef implementation
2180 */
2181 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2182 IPropertySetStorage *ppstg)
2183 {
2184 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2185 return IStorage_AddRef( &This->base.IStorage_iface );
2186 }
2187
2188 /************************************************************************
2189 * IPropertySetStorage_fnRelease (IUnknown)
2190 *
2191 * This method forwards to the common Release implementation
2192 */
2193 static ULONG WINAPI IPropertySetStorage_fnRelease(
2194 IPropertySetStorage *ppstg)
2195 {
2196 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2197 return IStorage_Release( &This->base.IStorage_iface );
2198 }
2199
2200 /************************************************************************
2201 * IPropertySetStorage_fnCreate (IPropertySetStorage)
2202 */
2203 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2204 IPropertySetStorage *ppstg,
2205 REFFMTID rfmtid,
2206 const CLSID* pclsid,
2207 DWORD grfFlags,
2208 DWORD grfMode,
2209 IPropertyStorage** ppprstg)
2210 {
2211 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2212 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2213 IStream *stm = NULL;
2214 HRESULT r;
2215
2216 TRACE("%p %s %08x %08x %p\n", This, debugstr_guid(rfmtid), grfFlags,
2217 grfMode, ppprstg);
2218
2219 /* be picky */
2220 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2221 {
2222 r = STG_E_INVALIDFLAG;
2223 goto end;
2224 }
2225
2226 if (!rfmtid)
2227 {
2228 r = E_INVALIDARG;
2229 goto end;
2230 }
2231
2232 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2233 * storage, not a stream. For now, disallow it.
2234 */
2235 if (grfFlags & PROPSETFLAG_NONSIMPLE)
2236 {
2237 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2238 r = STG_E_INVALIDFLAG;
2239 goto end;
2240 }
2241
2242 r = FmtIdToPropStgName(rfmtid, name);
2243 if (FAILED(r))
2244 goto end;
2245
2246 r = IStorage_CreateStream( &This->base.IStorage_iface, name, grfMode, 0, 0, &stm );
2247 if (FAILED(r))
2248 goto end;
2249
2250 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2251
2252 IStream_Release( stm );
2253
2254 end:
2255 TRACE("returning 0x%08x\n", r);
2256 return r;
2257 }
2258
2259 /************************************************************************
2260 * IPropertySetStorage_fnOpen (IPropertySetStorage)
2261 */
2262 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2263 IPropertySetStorage *ppstg,
2264 REFFMTID rfmtid,
2265 DWORD grfMode,
2266 IPropertyStorage** ppprstg)
2267 {
2268 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2269 IStream *stm = NULL;
2270 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2271 HRESULT r;
2272
2273 TRACE("%p %s %08x %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2274
2275 /* be picky */
2276 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2277 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2278 {
2279 r = STG_E_INVALIDFLAG;
2280 goto end;
2281 }
2282
2283 if (!rfmtid)
2284 {
2285 r = E_INVALIDARG;
2286 goto end;
2287 }
2288
2289 r = FmtIdToPropStgName(rfmtid, name);
2290 if (FAILED(r))
2291 goto end;
2292
2293 r = IStorage_OpenStream( &This->base.IStorage_iface, name, 0, grfMode, 0, &stm );
2294 if (FAILED(r))
2295 goto end;
2296
2297 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2298
2299 IStream_Release( stm );
2300
2301 end:
2302 TRACE("returning 0x%08x\n", r);
2303 return r;
2304 }
2305
2306 /************************************************************************
2307 * IPropertySetStorage_fnDelete (IPropertySetStorage)
2308 */
2309 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2310 IPropertySetStorage *ppstg,
2311 REFFMTID rfmtid)
2312 {
2313 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2314 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2315 HRESULT r;
2316
2317 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2318
2319 if (!rfmtid)
2320 return E_INVALIDARG;
2321
2322 r = FmtIdToPropStgName(rfmtid, name);
2323 if (FAILED(r))
2324 return r;
2325
2326 return IStorage_DestroyElement(&This->base.IStorage_iface, name);
2327 }
2328
2329 /************************************************************************
2330 * IPropertySetStorage_fnEnum (IPropertySetStorage)
2331 */
2332 static HRESULT WINAPI IPropertySetStorage_fnEnum(
2333 IPropertySetStorage *ppstg,
2334 IEnumSTATPROPSETSTG** ppenum)
2335 {
2336 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2337 return create_EnumSTATPROPSETSTG(This, ppenum);
2338 }
2339
2340 /************************************************************************
2341 * Implement IEnumSTATPROPSETSTG using enumx
2342 */
2343 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnQueryInterface(
2344 IEnumSTATPROPSETSTG *iface,
2345 REFIID riid,
2346 void** ppvObject)
2347 {
2348 return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2349 }
2350
2351 static ULONG WINAPI IEnumSTATPROPSETSTG_fnAddRef(
2352 IEnumSTATPROPSETSTG *iface)
2353 {
2354 return enumx_AddRef((enumx_impl*)iface);
2355 }
2356
2357 static ULONG WINAPI IEnumSTATPROPSETSTG_fnRelease(
2358 IEnumSTATPROPSETSTG *iface)
2359 {
2360 return enumx_Release((enumx_impl*)iface);
2361 }
2362
2363 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnNext(
2364 IEnumSTATPROPSETSTG *iface,
2365 ULONG celt,
2366 STATPROPSETSTG *rgelt,
2367 ULONG *pceltFetched)
2368 {
2369 return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2370 }
2371
2372 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnSkip(
2373 IEnumSTATPROPSETSTG *iface,
2374 ULONG celt)
2375 {
2376 return enumx_Skip((enumx_impl*)iface, celt);
2377 }
2378
2379 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnReset(
2380 IEnumSTATPROPSETSTG *iface)
2381 {
2382 return enumx_Reset((enumx_impl*)iface);
2383 }
2384
2385 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnClone(
2386 IEnumSTATPROPSETSTG *iface,
2387 IEnumSTATPROPSETSTG **ppenum)
2388 {
2389 return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2390 }
2391
2392 static HRESULT create_EnumSTATPROPSETSTG(
2393 StorageImpl *This,
2394 IEnumSTATPROPSETSTG** ppenum)
2395 {
2396 IStorage *stg = &This->base.IStorage_iface;
2397 IEnumSTATSTG *penum = NULL;
2398 STATSTG stat;
2399 ULONG count;
2400 HRESULT r;
2401 STATPROPSETSTG statpss;
2402 enumx_impl *enumx;
2403
2404 TRACE("%p %p\n", This, ppenum);
2405
2406 enumx = enumx_allocate(&IID_IEnumSTATPROPSETSTG,
2407 &IEnumSTATPROPSETSTG_Vtbl,
2408 sizeof (STATPROPSETSTG),
2409 (IUnknown*)&This->base.IStorage_iface,
2410 NULL);
2411
2412 /* add all the property set elements into a list */
2413 r = IStorage_EnumElements(stg, 0, NULL, 0, &penum);
2414 if (FAILED(r))
2415 {
2416 enumx_Release(enumx);
2417 return E_OUTOFMEMORY;
2418 }
2419
2420 while (1)
2421 {
2422 count = 0;
2423 r = IEnumSTATSTG_Next(penum, 1, &stat, &count);
2424 if (FAILED(r))
2425 break;
2426 if (!count)
2427 break;
2428 if (!stat.pwcsName)
2429 continue;
2430 if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM)
2431 {
2432 PropStgNameToFmtId(stat.pwcsName, &statpss.fmtid);
2433 TRACE("adding %s (%s)\n", debugstr_w(stat.pwcsName),
2434 debugstr_guid(&statpss.fmtid));
2435 statpss.mtime = stat.mtime;
2436 statpss.atime = stat.atime;
2437 statpss.ctime = stat.ctime;
2438 statpss.grfFlags = stat.grfMode;
2439 statpss.clsid = stat.clsid;
2440 enumx_add_element(enumx, &statpss);
2441 }
2442 CoTaskMemFree(stat.pwcsName);
2443 }
2444 IEnumSTATSTG_Release(penum);
2445
2446 *ppenum = (IEnumSTATPROPSETSTG*) enumx;
2447
2448 return S_OK;
2449 }
2450
2451 /************************************************************************
2452 * Implement IEnumSTATPROPSTG using enumx
2453 */
2454 static HRESULT WINAPI IEnumSTATPROPSTG_fnQueryInterface(
2455 IEnumSTATPROPSTG *iface,
2456 REFIID riid,
2457 void** ppvObject)
2458 {
2459 return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2460 }
2461
2462 static ULONG WINAPI IEnumSTATPROPSTG_fnAddRef(
2463 IEnumSTATPROPSTG *iface)
2464 {
2465 return enumx_AddRef((enumx_impl*)iface);
2466 }
2467
2468 static ULONG WINAPI IEnumSTATPROPSTG_fnRelease(
2469 IEnumSTATPROPSTG *iface)
2470 {
2471 return enumx_Release((enumx_impl*)iface);
2472 }
2473
2474 static HRESULT WINAPI IEnumSTATPROPSTG_fnNext(
2475 IEnumSTATPROPSTG *iface,
2476 ULONG celt,
2477 STATPROPSTG *rgelt,
2478 ULONG *pceltFetched)
2479 {
2480 return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2481 }
2482
2483 static HRESULT WINAPI IEnumSTATPROPSTG_fnSkip(
2484 IEnumSTATPROPSTG *iface,
2485 ULONG celt)
2486 {
2487 return enumx_Skip((enumx_impl*)iface, celt);
2488 }
2489
2490 static HRESULT WINAPI IEnumSTATPROPSTG_fnReset(
2491 IEnumSTATPROPSTG *iface)
2492 {
2493 return enumx_Reset((enumx_impl*)iface);
2494 }
2495
2496 static HRESULT WINAPI IEnumSTATPROPSTG_fnClone(
2497 IEnumSTATPROPSTG *iface,
2498 IEnumSTATPROPSTG **ppenum)
2499 {
2500 return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2501 }
2502
2503 static void prop_enum_copy_cb(IUnknown *parent, void *orig, void *dest)
2504 {
2505 PropertyStorage_impl *storage = impl_from_IPropertyStorage((IPropertyStorage*)parent);
2506 STATPROPSTG *src_prop = orig;
2507 STATPROPSTG *dest_prop = dest;
2508 LPWSTR name;
2509
2510 dest_prop->propid = src_prop->propid;
2511 dest_prop->vt = src_prop->vt;
2512 dest_prop->lpwstrName = NULL;
2513
2514 if (dictionary_find(storage->propid_to_name, UlongToPtr(src_prop->propid), (void**)&name))
2515 {
2516 DWORD size = (strlenW(name) + 1) * sizeof(WCHAR);
2517
2518 dest_prop->lpwstrName = CoTaskMemAlloc(size);
2519 if (!dest_prop->lpwstrName) return;
2520 memcpy(dest_prop->lpwstrName, name, size);
2521 }
2522 }
2523
2524 static BOOL prop_enum_stat(const void *k, const void *v, void *extra, void *arg)
2525 {
2526 enumx_impl *enumx = arg;
2527 PROPID propid = PtrToUlong(k);
2528 const PROPVARIANT *prop = v;
2529 STATPROPSTG stat;
2530
2531 stat.lpwstrName = NULL;
2532 stat.propid = propid;
2533 stat.vt = prop->vt;
2534
2535 enumx_add_element(enumx, &stat);
2536
2537 return TRUE;
2538 }
2539
2540 static HRESULT create_EnumSTATPROPSTG(
2541 PropertyStorage_impl *This,
2542 IEnumSTATPROPSTG** ppenum)
2543 {
2544 enumx_impl *enumx;
2545
2546 TRACE("%p %p\n", This, ppenum);
2547
2548 enumx = enumx_allocate(&IID_IEnumSTATPROPSTG,
2549 &IEnumSTATPROPSTG_Vtbl,
2550 sizeof (STATPROPSTG),
2551 (IUnknown*)&This->IPropertyStorage_iface,
2552 prop_enum_copy_cb);
2553
2554 dictionary_enumerate(This->propid_to_prop, prop_enum_stat, enumx);
2555
2556 *ppenum = (IEnumSTATPROPSTG*) enumx;
2557
2558 return S_OK;
2559 }
2560
2561 /***********************************************************************
2562 * vtables
2563 */
2564 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2565 {
2566 IPropertySetStorage_fnQueryInterface,
2567 IPropertySetStorage_fnAddRef,
2568 IPropertySetStorage_fnRelease,
2569 IPropertySetStorage_fnCreate,
2570 IPropertySetStorage_fnOpen,
2571 IPropertySetStorage_fnDelete,
2572 IPropertySetStorage_fnEnum
2573 };
2574
2575 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2576 {
2577 IPropertyStorage_fnQueryInterface,
2578 IPropertyStorage_fnAddRef,
2579 IPropertyStorage_fnRelease,
2580 IPropertyStorage_fnReadMultiple,
2581 IPropertyStorage_fnWriteMultiple,
2582 IPropertyStorage_fnDeleteMultiple,
2583 IPropertyStorage_fnReadPropertyNames,
2584 IPropertyStorage_fnWritePropertyNames,
2585 IPropertyStorage_fnDeletePropertyNames,
2586 IPropertyStorage_fnCommit,
2587 IPropertyStorage_fnRevert,
2588 IPropertyStorage_fnEnum,
2589 IPropertyStorage_fnSetTimes,
2590 IPropertyStorage_fnSetClass,
2591 IPropertyStorage_fnStat,
2592 };
2593
2594 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl =
2595 {
2596 IEnumSTATPROPSETSTG_fnQueryInterface,
2597 IEnumSTATPROPSETSTG_fnAddRef,
2598 IEnumSTATPROPSETSTG_fnRelease,
2599 IEnumSTATPROPSETSTG_fnNext,
2600 IEnumSTATPROPSETSTG_fnSkip,
2601 IEnumSTATPROPSETSTG_fnReset,
2602 IEnumSTATPROPSETSTG_fnClone,
2603 };
2604
2605 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl =
2606 {
2607 IEnumSTATPROPSTG_fnQueryInterface,
2608 IEnumSTATPROPSTG_fnAddRef,
2609 IEnumSTATPROPSTG_fnRelease,
2610 IEnumSTATPROPSTG_fnNext,
2611 IEnumSTATPROPSTG_fnSkip,
2612 IEnumSTATPROPSTG_fnReset,
2613 IEnumSTATPROPSTG_fnClone,
2614 };
2615
2616 /***********************************************************************
2617 * Format ID <-> name conversion
2618 */
2619 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
2620 'I','n','f','o','r','m','a','t','i','o','n',0 };
2621 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
2622 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
2623
2624 #define BITS_PER_BYTE 8
2625 #define CHARMASK 0x1f
2626 #define BITS_IN_CHARMASK 5
2627 #define NUM_ALPHA_CHARS 26
2628
2629 /***********************************************************************
2630 * FmtIdToPropStgName [ole32.@]
2631 * Returns the storage name of the format ID rfmtid.
2632 * PARAMS
2633 * rfmtid [I] Format ID for which to return a storage name
2634 * str [O] Storage name associated with rfmtid.
2635 *
2636 * RETURNS
2637 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
2638 *
2639 * NOTES
2640 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
2641 */
2642 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
2643 {
2644 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
2645
2646 TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
2647
2648 if (!rfmtid) return E_INVALIDARG;
2649 if (!str) return E_INVALIDARG;
2650
2651 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
2652 lstrcpyW(str, szSummaryInfo);
2653 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
2654 lstrcpyW(str, szDocSummaryInfo);
2655 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
2656 lstrcpyW(str, szDocSummaryInfo);
2657 else
2658 {
2659 const BYTE *fmtptr;
2660 WCHAR *pstr = str;
2661 ULONG bitsRemaining = BITS_PER_BYTE;
2662
2663 *pstr++ = 5;
2664 for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); )
2665 {
2666 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
2667
2668 if (bitsRemaining >= BITS_IN_CHARMASK)
2669 {
2670 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
2671 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
2672 *pstr <= 'z')
2673 *pstr += 'A' - 'a';
2674 pstr++;
2675 bitsRemaining -= BITS_IN_CHARMASK;
2676 if (bitsRemaining == 0)
2677 {
2678 fmtptr++;
2679 bitsRemaining = BITS_PER_BYTE;
2680 }
2681 }
2682 else
2683 {
2684 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
2685 i |= *fmtptr << bitsRemaining;
2686 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
2687 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
2688 }
2689 }
2690 *pstr = 0;
2691 }
2692 TRACE("returning %s\n", debugstr_w(str));
2693 return S_OK;
2694 }
2695
2696 /***********************************************************************
2697 * PropStgNameToFmtId [ole32.@]
2698 * Returns the format ID corresponding to the given name.
2699 * PARAMS
2700 * str [I] Storage name to convert to a format ID.
2701 * rfmtid [O] Format ID corresponding to str.
2702 *
2703 * RETURNS
2704 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
2705 * a format ID, S_OK otherwise.
2706 */
2707 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
2708 {
2709 HRESULT hr = STG_E_INVALIDNAME;
2710
2711 TRACE("%s, %p\n", debugstr_w(str), rfmtid);
2712
2713 if (!rfmtid) return E_INVALIDARG;
2714 if (!str) return STG_E_INVALIDNAME;
2715
2716 if (!lstrcmpiW(str, szDocSummaryInfo))
2717 {
2718 *rfmtid = FMTID_DocSummaryInformation;
2719 hr = S_OK;
2720 }
2721 else if (!lstrcmpiW(str, szSummaryInfo))
2722 {
2723 *rfmtid = FMTID_SummaryInformation;
2724 hr = S_OK;
2725 }
2726 else
2727 {
2728 ULONG bits;
2729 BYTE *fmtptr = (BYTE *)rfmtid - 1;
2730 const WCHAR *pstr = str;
2731
2732 memset(rfmtid, 0, sizeof(*rfmtid));
2733 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
2734 bits += BITS_IN_CHARMASK)
2735 {
2736 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
2737 WCHAR wc;
2738
2739 if (bitsUsed == 0)
2740 fmtptr++;
2741 wc = *++pstr - 'A';
2742 if (wc > NUM_ALPHA_CHARS)
2743 {
2744 wc += 'A' - 'a';
2745 if (wc > NUM_ALPHA_CHARS)
2746 {
2747 wc += 'a' - '0' + NUM_ALPHA_CHARS;
2748 if (wc > CHARMASK)
2749 {
2750 WARN("invalid character (%d)\n", *pstr);
2751 goto end;
2752 }
2753 }
2754 }
2755 *fmtptr |= wc << bitsUsed;
2756 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
2757 if (bitsStored < BITS_IN_CHARMASK)
2758 {
2759 wc >>= BITS_PER_BYTE - bitsUsed;
2760 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
2761 {
2762 if (wc != 0)
2763 {
2764 WARN("extra bits\n");
2765 goto end;
2766 }
2767 break;
2768 }
2769 fmtptr++;
2770 *fmtptr |= (BYTE)wc;
2771 }
2772 }
2773 hr = S_OK;
2774 }
2775 end:
2776 return hr;
2777 }
2778
2779 #ifdef __i386__ /* thiscall functions are i386-specific */
2780
2781 #define DEFINE_STDCALL_WRAPPER(num,func,args) \
2782 __ASM_STDCALL_FUNC(func, args, \
2783 "popl %eax\n\t" \
2784 "popl %ecx\n\t" \
2785 "pushl %eax\n\t" \
2786 "movl (%ecx), %eax\n\t" \
2787 "jmp *(4*(" #num "))(%eax)" )
2788
2789 DEFINE_STDCALL_WRAPPER(0,Allocate_PMemoryAllocator,8)
2790 extern void* __stdcall Allocate_PMemoryAllocator(void *this, ULONG cbSize);
2791
2792 #else
2793
2794 static void* __cdecl Allocate_PMemoryAllocator(void *this, ULONG cbSize)
2795 {
2796 void* (__cdecl *fn)(void*,ULONG) = **(void***)this;
2797 return fn(this, cbSize);
2798 }
2799
2800 #endif
2801
2802 BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop,
2803 USHORT CodePage, PROPVARIANT* pvar, void* pma)
2804 {
2805 HRESULT hr;
2806
2807 hr = PropertyStorage_ReadProperty(pvar, (const BYTE*)prop, CodePage, Allocate_PMemoryAllocator, pma);
2808
2809 if (FAILED(hr))
2810 {
2811 FIXME("should raise C++ exception on failure\n");
2812 PropVariantInit(pvar);
2813 }
2814
2815 return FALSE;
2816 }
2817
2818 SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *pvar,
2819 USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid,
2820 BOOLEAN fReserved, ULONG *pcIndirect)
2821 {
2822 FIXME("%p,%d,%p,%p,%d,%d,%p\n", pvar, CodePage, pprop, pcb, pid, fReserved, pcIndirect);
2823
2824 return NULL;
2825 }
2826
2827 HRESULT WINAPI StgCreatePropStg(IUnknown *unk, REFFMTID fmt, const CLSID *clsid,
2828 DWORD flags, DWORD reserved, IPropertyStorage **prop_stg)
2829 {
2830 IStorage *stg;
2831 IStream *stm;
2832 HRESULT r;
2833
2834 TRACE("%p %s %s %08x %d %p\n", unk, debugstr_guid(fmt), debugstr_guid(clsid), flags, reserved, prop_stg);
2835
2836 if (!fmt || reserved)
2837 {
2838 r = E_INVALIDARG;
2839 goto end;
2840 }
2841
2842 if (flags & PROPSETFLAG_NONSIMPLE)
2843 {
2844 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
2845 if (FAILED(r))
2846 goto end;
2847
2848 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to create a
2849 * storage, not a stream. For now, disallow it.
2850 */
2851 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2852 IStorage_Release(stg);
2853 r = STG_E_INVALIDFLAG;
2854 }
2855 else
2856 {
2857 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
2858 if (FAILED(r))
2859 goto end;
2860
2861 r = PropertyStorage_ConstructEmpty(stm, fmt, flags,
2862 STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
2863
2864 IStream_Release( stm );
2865 }
2866
2867 end:
2868 TRACE("returning 0x%08x\n", r);
2869 return r;
2870 }
2871
2872 HRESULT WINAPI StgOpenPropStg(IUnknown *unk, REFFMTID fmt, DWORD flags,
2873 DWORD reserved, IPropertyStorage **prop_stg)
2874 {
2875 IStorage *stg;
2876 IStream *stm;
2877 HRESULT r;
2878
2879 TRACE("%p %s %08x %d %p\n", unk, debugstr_guid(fmt), flags, reserved, prop_stg);
2880
2881 if (!fmt || reserved)
2882 {
2883 r = E_INVALIDARG;
2884 goto end;
2885 }
2886
2887 if (flags & PROPSETFLAG_NONSIMPLE)
2888 {
2889 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
2890 if (FAILED(r))
2891 goto end;
2892
2893 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to open a
2894 * storage, not a stream. For now, disallow it.
2895 */
2896 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2897 IStorage_Release(stg);
2898 r = STG_E_INVALIDFLAG;
2899 }
2900 else
2901 {
2902 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
2903 if (FAILED(r))
2904 goto end;
2905
2906 r = PropertyStorage_ConstructFromStream(stm, fmt,
2907 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
2908
2909 IStream_Release( stm );
2910 }
2911
2912 end:
2913 TRACE("returning 0x%08x\n", r);
2914 return r;
2915 }