Handle wParam in WM_PAINT properly: if non-null, it is the hdc we are
[wine] / dlls / 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27  *
28  * There's a decent overview of property set storage here:
29  * http://msdn.microsoft.com/archive/en-us/dnarolegen/html/msdn_propset.asp
30  * It's a little bit out of date, and more definitive references are given
31  * below, but it gives the best "big picture" that I've found.
32  *
33  * TODO: There's a lot missing in here.  Biggies:
34  * - There are all sorts of restricions I don't honor, like maximum property
35  *   set byte size, maximum property name length
36  * - Certain bogus files could result in reading past the end of a buffer.
37  * - Write support is missing.
38  * - This will probably fail on big-endian machines, especially reading and
39  *   writing strings.
40  * - Mac-generated files won't be read correctly, even if they're little
41  *   endian, because I disregard whether the generator was a Mac.  This means
42  *   strings will probably be munged (as I don't understand Mac scripts.)
43  * - Not all PROPVARIANT types are supported.
44  * There are lots more unimplemented features, see FIXMEs below.
45  */
46
47 #include <assert.h>
48 #include <stdarg.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52
53 #define COBJMACROS
54 #define NONAMELESSUNION
55 #define NONAMELESSSTRUCT
56
57 #include "windef.h"
58 #include "winbase.h"
59 #include "winnls.h"
60 #include "winuser.h"
61 #include "wine/unicode.h"
62 #include "wine/debug.h"
63 #include "dictionary.h"
64 #include "storage32.h"
65
66 WINE_DEFAULT_DEBUG_CHANNEL(storage);
67
68 #define _IPropertySetStorage_Offset ((int)(&(((StorageImpl*)0)->base.pssVtbl)))
69 #define _ICOM_THIS_From_IPropertySetStorage(class, name) \
70     class* This = (class*)(((char*)name)-_IPropertySetStorage_Offset)
71
72 /* These are documented in MSDN, e.g.
73  * http://msdn.microsoft.com/library/en-us/stg/stg/property_set_header.asp
74  * http://msdn.microsoft.com/library/library/en-us/stg/stg/section.asp
75  * but they don't seem to be in any header file.
76  */
77 #define PROPSETHDR_BYTEORDER_MAGIC      0xfffe
78 #define PROPSETHDR_OSVER_KIND_WIN16     0
79 #define PROPSETHDR_OSVER_KIND_MAC       1
80 #define PROPSETHDR_OSVER_KIND_WIN32     2
81
82 /* The format version (and what it implies) is described here:
83  * http://msdn.microsoft.com/library/en-us/stg/stg/format_version.asp
84  */
85 typedef struct tagPROPERTYSETHEADER
86 {
87     WORD  wByteOrder; /* always 0xfffe */
88     WORD  wFormat;    /* can be zero or one */
89     DWORD dwOSVer;    /* OS version of originating system */
90     CLSID clsid;      /* application CLSID */
91     DWORD reserved;   /* always 1 */
92 } PROPERTYSETHEADER;
93
94 typedef struct tagFORMATIDOFFSET
95 {
96     FMTID fmtid;
97     DWORD dwOffset; /* from beginning of stream */
98 } FORMATIDOFFSET;
99
100 typedef struct tagPROPERTYSECTIONHEADER
101 {
102     DWORD cbSection;
103     DWORD cProperties;
104 } PROPERTYSECTIONHEADER;
105
106 typedef struct tagPROPERTYIDOFFSET
107 {
108     DWORD propid;
109     DWORD dwOffset;
110 } PROPERTYIDOFFSET;
111
112 /* Initializes the property storage from the stream (and undoes any uncommitted
113  * changes in the process.)  Returns an error if there is an error reading or
114  * if the stream format doesn't match what's expected.
115  */
116 static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface);
117
118 static HRESULT PropertyStorage_WriteToStream(IPropertyStorage *iface);
119
120 static IPropertyStorageVtbl IPropertyStorage_Vtbl;
121
122 /***********************************************************************
123  * Implementation of IPropertyStorage
124  */
125 typedef struct tagPropertyStorage_impl
126 {
127     IPropertyStorageVtbl *vtbl;
128     DWORD ref;
129     CRITICAL_SECTION cs;
130     IStream *stm;
131     BOOL  dirty;
132     FMTID fmtid;
133     CLSID clsid;
134     DWORD originatorOS;
135     DWORD grfFlags;
136     DWORD grfMode;
137     UINT  codePage;
138     LCID  locale;
139     PROPID highestProp;
140     struct dictionary *name_to_propid;
141     struct dictionary *propid_to_name;
142     struct dictionary *propid_to_prop;
143 } PropertyStorage_impl;
144
145 /************************************************************************
146  * IPropertyStorage_fnQueryInterface (IPropertyStorage)
147  */
148 static HRESULT WINAPI IPropertyStorage_fnQueryInterface(
149     IPropertyStorage *iface,
150     REFIID riid,
151     void** ppvObject)
152 {
153     PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
154
155     if ( (This==0) || (ppvObject==0) )
156         return E_INVALIDARG;
157
158     *ppvObject = 0;
159
160     if (IsEqualGUID(&IID_IUnknown, riid) ||
161         IsEqualGUID(&IID_IPropertyStorage, riid))
162     {
163         IPropertyStorage_AddRef(iface);
164         *ppvObject = (IPropertyStorage*)iface;
165         return S_OK;
166     }
167
168     return E_NOINTERFACE;
169 }
170
171 /************************************************************************
172  * IPropertyStorage_fnAddRef (IPropertyStorage)
173  */
174 static ULONG WINAPI IPropertyStorage_fnAddRef(
175     IPropertyStorage *iface)
176 {
177     PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
178     return InterlockedIncrement(&This->ref);
179 }
180
181 /************************************************************************
182  * IPropertyStorage_fnRelease (IPropertyStorage)
183  */
184 static ULONG WINAPI IPropertyStorage_fnRelease(
185     IPropertyStorage *iface)
186 {
187     PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
188     ULONG ref;
189
190     ref = InterlockedDecrement(&This->ref);
191     if (ref == 0)
192     {
193         TRACE("Destroying %p\n", This);
194         IStream_Release(This->stm);
195         DeleteCriticalSection(&This->cs);
196         dictionary_destroy(This->name_to_propid);
197         dictionary_destroy(This->propid_to_name);
198         dictionary_destroy(This->propid_to_prop);
199         HeapFree(GetProcessHeap(), 0, This);
200     }
201     return ref;
202 }
203
204 static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
205  DWORD propid)
206 {
207     PROPVARIANT *ret = NULL;
208
209     if (!This)
210         return NULL;
211     dictionary_find(This->propid_to_prop, (void *)propid, (void **)&ret);
212     return ret;
213 }
214
215 static PROPVARIANT *PropertyStorage_FindPropertyByName(
216  PropertyStorage_impl *This, LPCWSTR name)
217 {
218     PROPVARIANT *ret = NULL;
219     PROPID propid;
220
221     if (!This || !name)
222         return NULL;
223     if (dictionary_find(This->name_to_propid, name, (void **)&propid))
224         ret = PropertyStorage_FindProperty(This, (PROPID)propid);
225     return ret;
226 }
227
228 static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
229  DWORD propid)
230 {
231     LPWSTR ret = NULL;
232
233     if (!This)
234         return NULL;
235     dictionary_find(This->propid_to_name, (void *)propid, (void **)&ret);
236     return ret;
237 }
238
239 /************************************************************************
240  * IPropertyStorage_fnReadMultiple (IPropertyStorage)
241  */
242 static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
243     IPropertyStorage* iface,
244     ULONG cpspec,
245     const PROPSPEC rgpspec[],
246     PROPVARIANT rgpropvar[])
247 {
248     PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
249     HRESULT hr = S_OK;
250     ULONG i;
251
252     TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
253     if (!This)
254         return E_INVALIDARG;
255     if (cpspec && (!rgpspec || !rgpropvar))
256         return E_INVALIDARG;
257     EnterCriticalSection(&This->cs);
258     for (i = 0; i < cpspec; i++)
259     {
260         PropVariantInit(&rgpropvar[i]);
261         if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
262         {
263             PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
264              rgpspec[i].u.lpwstr);
265
266             if (prop)
267                 PropVariantCopy(&rgpropvar[i], prop);
268         }
269         else
270         {
271             PROPVARIANT *prop = PropertyStorage_FindProperty(This,
272              rgpspec[i].u.propid);
273
274             if (prop)
275                 PropVariantCopy(&rgpropvar[i], prop);
276         }
277     }
278     LeaveCriticalSection(&This->cs);
279     return hr;
280 }
281
282 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
283  PROPID propid, const PROPVARIANT *propvar)
284 {
285     HRESULT hr = S_OK;
286     PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
287
288     if (prop)
289     {
290         PropVariantClear(prop);
291         PropVariantCopy(prop, propvar);
292     }
293     else
294     {
295         prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
296          sizeof(PROPVARIANT));
297         if (prop)
298         {
299             PropVariantCopy(prop, propvar);
300             dictionary_insert(This->propid_to_prop, (void *)propid, prop);
301         }
302         else
303             hr = STG_E_INSUFFICIENTMEMORY;
304     }
305     return hr;
306 }
307
308 /************************************************************************
309  * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
310  */
311 static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
312     IPropertyStorage* iface,
313     ULONG cpspec,
314     const PROPSPEC rgpspec[],
315     const PROPVARIANT rgpropvar[],
316     PROPID propidNameFirst)
317 {
318     PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
319     HRESULT hr = S_OK;
320     ULONG i;
321
322     TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
323     if (!This)
324         return E_INVALIDARG;
325     if (cpspec && (!rgpspec || !rgpropvar))
326         return E_INVALIDARG;
327     if (!(This->grfMode & STGM_READWRITE))
328         return STG_E_ACCESSDENIED;
329     EnterCriticalSection(&This->cs);
330     This->dirty = TRUE;
331     This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
332      PROPSETHDR_OSVER_KIND_WIN32) ;
333     for (i = 0; i < cpspec; i++)
334     {
335         if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
336         {
337             PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
338              rgpspec[i].u.lpwstr);
339
340             if (prop)
341                 PropVariantCopy(prop, &rgpropvar[i]);
342             else
343             {
344                 if (propidNameFirst < PID_FIRST_USABLE ||
345                  propidNameFirst >= PID_MIN_READONLY)
346                     hr = STG_E_INVALIDPARAMETER;
347                 else
348                 {
349                     PROPID nextId = max(propidNameFirst, This->highestProp + 1);
350                     size_t len = strlenW(rgpspec[i].u.lpwstr) + 1;
351                     LPWSTR name = HeapAlloc(GetProcessHeap(), 0,
352                      len * sizeof(WCHAR));
353
354                     strcpyW(name, rgpspec[i].u.lpwstr);
355                     dictionary_insert(This->name_to_propid, name,
356                      (void *)nextId);
357                     dictionary_insert(This->propid_to_name, (void *)nextId,
358                      name);
359                     This->highestProp = nextId;
360                     hr = PropertyStorage_StorePropWithId(This, nextId,
361                      &rgpropvar[i]);
362                 }
363             }
364         }
365         else
366         {
367             /* FIXME: certain propid's have special behavior.  E.g., you can't
368              * set propid 0, and setting PID_BEHAVIOR affects the
369              * case-sensitivity.
370              */
371             hr = PropertyStorage_StorePropWithId(This, rgpspec[i].u.propid,
372              &rgpropvar[i]);
373         }
374     }
375     LeaveCriticalSection(&This->cs);
376     return hr;
377 }
378
379 /************************************************************************
380  * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
381  */
382 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
383     IPropertyStorage* iface,
384     ULONG cpspec,
385     const PROPSPEC rgpspec[])
386 {
387     PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
388     ULONG i;
389     HRESULT hr;
390
391     TRACE("(%p, %ld, %p)\n", iface, cpspec, rgpspec);
392     if (!This)
393         return E_INVALIDARG;
394     if (!rgpspec)
395         return E_INVALIDARG;
396     if (!(This->grfMode & STGM_READWRITE))
397         return STG_E_ACCESSDENIED;
398     hr = S_OK;
399     EnterCriticalSection(&This->cs);
400     for (i = 0; i < cpspec; i++)
401     {
402         if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
403         {
404             PROPID propid;
405
406             if (dictionary_find(This->name_to_propid,
407              (void *)rgpspec[i].u.lpwstr, (void **)&propid))
408                 dictionary_remove(This->propid_to_prop, (void *)propid);
409         }
410         else
411         {
412             /* FIXME: certain propid's have special meaning.  For example,
413              * removing propid 0 is supposed to remove the dictionary, and
414              * removing PID_BEHAVIOR should change this to a case-insensitive
415              * property set.  Unknown "read-only" propid's should be ignored.
416              */
417             dictionary_remove(This->propid_to_prop,
418              (void *)rgpspec[i].u.propid);
419         }
420     }
421     LeaveCriticalSection(&This->cs);
422     return hr;
423 }
424
425 /************************************************************************
426  * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
427  */
428 static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
429     IPropertyStorage* iface,
430     ULONG cpropid,
431     const PROPID rgpropid[],
432     LPOLESTR rglpwstrName[])
433 {
434     PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
435     ULONG i;
436     HRESULT hr;
437
438     TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
439     if (!This)
440         return E_INVALIDARG;
441     if (cpropid && (!rgpropid || !rglpwstrName))
442         return E_INVALIDARG;
443     /* MSDN says S_FALSE is returned if no strings matching rgpropid are found,
444      * default to that
445      */
446     hr = S_FALSE;
447     EnterCriticalSection(&This->cs);
448     for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
449     {
450         LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
451
452         if (name)
453         {
454             size_t len = lstrlenW(name);
455
456             hr = S_OK;
457             rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
458             if (rglpwstrName)
459                 memcpy(rglpwstrName, name, (len + 1) * sizeof(WCHAR));
460             else
461                 hr = STG_E_INSUFFICIENTMEMORY;
462         }
463         else
464             rglpwstrName[i] = NULL;
465     }
466     LeaveCriticalSection(&This->cs);
467     return hr;
468 }
469
470 /************************************************************************
471  * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
472  */
473 static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
474     IPropertyStorage* iface,
475     ULONG cpropid,
476     const PROPID rgpropid[],
477     const LPOLESTR rglpwstrName[])
478 {
479     PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
480     ULONG i;
481     HRESULT hr;
482
483     TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
484     if (!This)
485         return E_INVALIDARG;
486     if (cpropid && (!rgpropid || !rglpwstrName))
487         return E_INVALIDARG;
488     if (!(This->grfMode & STGM_READWRITE))
489         return STG_E_ACCESSDENIED;
490     hr = S_OK;
491     EnterCriticalSection(&This->cs);
492     for (i = 0; i < cpropid; i++)
493     {
494         if (rgpropid[i] != PID_ILLEGAL)
495         {
496             size_t len = lstrlenW(rglpwstrName[i] + 1);
497             LPWSTR name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
498
499             strcpyW(name, rglpwstrName[i]);
500             dictionary_insert(This->name_to_propid, name, (void *)rgpropid[i]);
501             dictionary_insert(This->propid_to_name, (void *)rgpropid[i], name);
502         }
503     }
504     LeaveCriticalSection(&This->cs);
505     return hr;
506 }
507
508 /************************************************************************
509  * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
510  */
511 static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
512     IPropertyStorage* iface,
513     ULONG cpropid,
514     const PROPID rgpropid[])
515 {
516     PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
517     ULONG i;
518     HRESULT hr;
519
520     TRACE("(%p, %ld, %p)\n", iface, cpropid, rgpropid);
521     if (!This)
522         return E_INVALIDARG;
523     if (cpropid && !rgpropid)
524         return E_INVALIDARG;
525     if (!(This->grfMode & STGM_READWRITE))
526         return STG_E_ACCESSDENIED;
527     hr = S_OK;
528     EnterCriticalSection(&This->cs);
529     for (i = 0; i < cpropid; i++)
530     {
531         LPWSTR name = NULL;
532
533         if (dictionary_find(This->propid_to_name, (void *)rgpropid[i],
534          (void **)&name))
535         {
536             dictionary_remove(This->propid_to_name, (void *)rgpropid[i]);
537             dictionary_remove(This->name_to_propid, name);
538         }
539     }
540     LeaveCriticalSection(&This->cs);
541     return hr;
542 }
543
544 /************************************************************************
545  * IPropertyStorage_fnCommit (IPropertyStorage)
546  */
547 static HRESULT WINAPI IPropertyStorage_fnCommit(
548     IPropertyStorage* iface,
549     DWORD grfCommitFlags)
550 {
551     PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
552     HRESULT hr;
553
554     TRACE("(%p, 0x%08lx)\n", iface, grfCommitFlags);
555     if (!This)
556         return E_INVALIDARG;
557     if (!(This->grfMode & STGM_READWRITE))
558         return STG_E_ACCESSDENIED;
559     EnterCriticalSection(&This->cs);
560     if (This->dirty)
561         hr = PropertyStorage_WriteToStream(iface);
562     else
563         hr = S_OK;
564     LeaveCriticalSection(&This->cs);
565     return hr;
566 }
567
568 /************************************************************************
569  * IPropertyStorage_fnRevert (IPropertyStorage)
570  */
571 static HRESULT WINAPI IPropertyStorage_fnRevert(
572     IPropertyStorage* iface)
573 {
574     HRESULT hr;
575     PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
576
577     TRACE("%p\n", iface);
578     if (!This)
579         return E_INVALIDARG;
580
581     EnterCriticalSection(&This->cs);
582     if (This->dirty)
583         hr = PropertyStorage_ReadFromStream(iface);
584     else
585         hr = S_OK;
586     LeaveCriticalSection(&This->cs);
587     return hr;
588 }
589
590 /************************************************************************
591  * IPropertyStorage_fnEnum (IPropertyStorage)
592  */
593 static HRESULT WINAPI IPropertyStorage_fnEnum(
594     IPropertyStorage* iface,
595     IEnumSTATPROPSTG** ppenum)
596 {
597     FIXME("\n");
598     return E_NOTIMPL;
599 }
600
601 /************************************************************************
602  * IPropertyStorage_fnSetTimes (IPropertyStorage)
603  */
604 static HRESULT WINAPI IPropertyStorage_fnSetTimes(
605     IPropertyStorage* iface,
606     const FILETIME* pctime,
607     const FILETIME* patime,
608     const FILETIME* pmtime)
609 {
610     FIXME("\n");
611     return E_NOTIMPL;
612 }
613
614 /************************************************************************
615  * IPropertyStorage_fnSetClass (IPropertyStorage)
616  */
617 static HRESULT WINAPI IPropertyStorage_fnSetClass(
618     IPropertyStorage* iface,
619     REFCLSID clsid)
620 {
621     PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
622
623     TRACE("%p, %s\n", iface, debugstr_guid(clsid));
624     if (!This || !clsid)
625         return E_INVALIDARG;
626     if (!(This->grfMode & STGM_READWRITE))
627         return STG_E_ACCESSDENIED;
628     memcpy(&This->clsid, clsid, sizeof(This->clsid));
629     This->dirty = TRUE;
630     return S_OK;
631 }
632
633 /************************************************************************
634  * IPropertyStorage_fnStat (IPropertyStorage)
635  */
636 static HRESULT WINAPI IPropertyStorage_fnStat(
637     IPropertyStorage* iface,
638     STATPROPSETSTG* statpsstg)
639 {
640     PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
641     STATSTG stat;
642     HRESULT hr;
643
644     TRACE("%p, %p\n", iface, statpsstg);
645     if (!This || !statpsstg)
646         return E_INVALIDARG;
647
648     hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
649     if (SUCCEEDED(hr))
650     {
651         memcpy(&statpsstg->fmtid, &This->fmtid, sizeof(statpsstg->fmtid));
652         memcpy(&statpsstg->clsid, &This->clsid, sizeof(statpsstg->clsid));
653         statpsstg->grfFlags = This->grfFlags;
654         memcpy(&statpsstg->mtime, &stat.mtime, sizeof(statpsstg->mtime));
655         memcpy(&statpsstg->ctime, &stat.ctime, sizeof(statpsstg->ctime));
656         memcpy(&statpsstg->atime, &stat.atime, sizeof(statpsstg->atime));
657         statpsstg->dwOSVersion = This->originatorOS;
658     }
659     return hr;
660 }
661
662 static int PropertyStorage_PropNameCompare(const void *a, const void *b,
663  void *extra)
664 {
665     PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
666
667     if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
668         return strcmpW((LPCWSTR)a, (LPCWSTR)b);
669     else
670         return strcmpiW((LPCWSTR)a, (LPCWSTR)b);
671 }
672
673 static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra)
674 {
675     HeapFree(GetProcessHeap(), 0, k);
676 }
677
678 static int PropertyStorage_PropCompare(const void *a, const void *b,
679  void *extra)
680 {
681     return (PROPID)a - (PROPID)b;
682 }
683
684 static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra)
685 {
686     PropVariantClear((PROPVARIANT *)d);
687     HeapFree(GetProcessHeap(), 0, d);
688 }
689
690 /* Reads the dictionary from the memory buffer beginning at ptr.  Interprets
691  * the entries according to the values of This->codePage and This->locale.
692  * FIXME: there isn't any checking whether the read property extends past the
693  * end of the buffer.
694  */
695 static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
696  BYTE *ptr)
697 {
698     DWORD numEntries = *(DWORD *)ptr;
699     HRESULT hr = S_OK;
700
701     ptr += sizeof(DWORD);
702     This->name_to_propid = dictionary_create(PropertyStorage_PropNameCompare,
703      PropertyStorage_PropNameDestroy, This);
704     This->propid_to_name = dictionary_create(PropertyStorage_PropCompare, NULL,
705      This);
706     if (This->name_to_propid && This->propid_to_name)
707     {
708         DWORD i;
709
710         for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
711         {
712             PROPID propid;
713             DWORD cbEntry;
714             int len;
715
716             StorageUtl_ReadDWord(ptr, 0, &propid);
717             ptr += sizeof(PROPID);
718             StorageUtl_ReadDWord(ptr, 0, &cbEntry);
719             ptr += sizeof(DWORD);
720             /* FIXME: if host is big-endian, this'll suck to convert */
721             len = MultiByteToWideChar(This->codePage, 0, ptr, cbEntry, NULL, 0);
722             if (!len)
723                 hr = HRESULT_FROM_WIN32(GetLastError());
724             else
725             {
726                 LPWSTR name = HeapAlloc(GetProcessHeap(), 0,
727                  len * sizeof(WCHAR));
728
729                 if (name)
730                 {
731                     MultiByteToWideChar(This->codePage, 0, ptr + sizeof(DWORD),
732                      cbEntry, name, len);
733                     dictionary_insert(This->name_to_propid, name,
734                      (void *)propid);
735                     dictionary_insert(This->propid_to_name, (void *)propid,
736                      name);
737                     TRACE("Property %s maps to id %ld\n", debugstr_w(name),
738                      propid);
739                 }
740                 else
741                     hr = STG_E_INSUFFICIENTMEMORY;
742             }
743             ptr += sizeof(DWORD) + cbEntry;
744             /* Unicode entries are padded to DWORD boundaries */
745             if (This->codePage == 1200 && cbEntry % sizeof(DWORD))
746                 ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
747         }
748     }
749     else
750     {
751         /* one or the other failed, free the other */
752         if (This->name_to_propid)
753         {
754             dictionary_destroy(This->name_to_propid);
755             This->name_to_propid = NULL;
756         }
757         if (This->propid_to_name)
758         {
759             dictionary_destroy(This->propid_to_name);
760             This->propid_to_name = NULL;
761         }
762         hr = STG_E_INSUFFICIENTMEMORY;
763     }
764     return hr;
765 }
766
767 /* FIXME: there isn't any checking whether the read property extends past the
768  * end of the buffer.
769  */
770 static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, DWORD type,
771  const BYTE *data)
772 {
773     HRESULT hr = S_OK;
774
775     prop->vt = VT_EMPTY;
776     switch (type)
777     {
778     case VT_NULL:
779         prop->vt = VT_NULL;
780         break;
781     case VT_I1:
782         prop->vt = VT_I1;
783         prop->u.cVal = *(const char *)data;
784         TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
785         break;
786     case VT_UI1:
787         prop->vt = VT_UI1;
788         prop->u.bVal = *(const UCHAR *)data;
789         TRACE("Read byte 0x%x\n", prop->u.bVal);
790         break;
791     case VT_I2:
792         prop->vt = VT_I2;
793         StorageUtl_ReadWord(data, 0, &prop->u.iVal);
794         TRACE("Read short %d\n", prop->u.iVal);
795         break;
796     case VT_UI2:
797         prop->vt = VT_UI2;
798         StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
799         TRACE("Read ushort %d\n", prop->u.uiVal);
800         break;
801     case VT_I4:
802         prop->vt = VT_I4;
803         StorageUtl_ReadDWord(data, 0, &prop->u.lVal);
804         TRACE("Read long %ld\n", prop->u.lVal);
805         break;
806     case VT_UI4:
807         prop->vt = VT_UI4;
808         StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
809         TRACE("Read ulong %ld\n", prop->u.ulVal);
810         break;
811     case VT_LPSTR:
812     {
813         DWORD count = *(const DWORD *)data;
814
815         prop->u.pszVal = CoTaskMemAlloc(count);
816         if (prop->u.pszVal)
817         {
818             /* FIXME: if the host is big-endian, this'll suck */
819             memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
820             prop->vt = VT_LPSTR;
821             /* FIXME: so far so good, but this may be Unicode or DBCS depending
822              * on This->codePage.
823              */
824             TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
825         }
826         else
827             hr = STG_E_INSUFFICIENTMEMORY;
828         break;
829     }
830     case VT_LPWSTR:
831     {
832         DWORD count = *(DWORD *)data;
833
834         prop->u.pwszVal = CoTaskMemAlloc(count * sizeof(WCHAR));
835         if (prop->u.pwszVal)
836         {
837             /* FIXME: if the host is big-endian, gotta swap every char */
838             memcpy(prop->u.pwszVal, data + sizeof(DWORD),
839              count * sizeof(WCHAR));
840             prop->vt = VT_LPWSTR;
841             TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
842         }
843         else
844             hr = STG_E_INSUFFICIENTMEMORY;
845         break;
846     }
847     case VT_FILETIME:
848         /* FIXME: endianness */
849         prop->vt = VT_FILETIME;
850         memcpy(&prop->u.filetime, data, sizeof(FILETIME));
851         break;
852     default:
853         FIXME("unsupported type %ld\n", type);
854         hr = STG_E_INVALIDPARAMETER;
855     }
856     return hr;
857 }
858
859 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
860  PROPERTYSETHEADER *hdr)
861 {
862     BYTE buf[sizeof(PROPERTYSETHEADER)];
863     ULONG count = 0;
864     HRESULT hr;
865
866     hr = IStream_Read(stm, buf, sizeof(buf), &count);
867     if (SUCCEEDED(hr))
868     {
869         if (count != sizeof(buf))
870         {
871             WARN("read %ld, expected %d\n", count, sizeof(buf));
872             hr = STG_E_INVALIDHEADER;
873         }
874         else
875         {
876             StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
877              &hdr->wByteOrder);
878             StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
879              &hdr->wFormat);
880             StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
881              &hdr->dwOSVer);
882             StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
883              &hdr->clsid);
884             StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
885              &hdr->reserved);
886         }
887     }
888     TRACE("returning 0x%08lx\n", hr);
889     return hr;
890 }
891
892 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
893  FORMATIDOFFSET *fmt)
894 {
895     BYTE buf[sizeof(FORMATIDOFFSET)];
896     ULONG count = 0;
897     HRESULT hr;
898
899     hr = IStream_Read(stm, buf, sizeof(buf), &count);
900     if (SUCCEEDED(hr))
901     {
902         if (count != sizeof(buf))
903         {
904             WARN("read %ld, expected %d\n", count, sizeof(buf));
905             hr = STG_E_INVALIDHEADER;
906         }
907         else
908         {
909             StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
910              &fmt->fmtid);
911             StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
912              &fmt->dwOffset);
913         }
914     }
915     TRACE("returning 0x%08lx\n", hr);
916     return hr;
917 }
918
919 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
920  PROPERTYSECTIONHEADER *hdr)
921 {
922     BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
923     ULONG count = 0;
924     HRESULT hr;
925
926     hr = IStream_Read(stm, buf, sizeof(buf), &count);
927     if (SUCCEEDED(hr))
928     {
929         if (count != sizeof(buf))
930         {
931             WARN("read %ld, expected %d\n", count, sizeof(buf));
932             hr = STG_E_INVALIDHEADER;
933         }
934         else
935         {
936             StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
937              cbSection), &hdr->cbSection);
938             StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
939              cProperties), &hdr->cProperties);
940         }
941     }
942     TRACE("returning 0x%08lx\n", hr);
943     return hr;
944 }
945
946 static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface)
947 {
948     PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
949     PROPERTYSETHEADER hdr;
950     FORMATIDOFFSET fmtOffset;
951     PROPERTYSECTIONHEADER sectionHdr;
952     ULONG i;
953     STATSTG stat;
954     HRESULT hr;
955     BYTE *buf = NULL;
956     ULONG count = 0;
957     DWORD dictOffset = 0;
958
959     if (!This)
960         return E_INVALIDARG;
961
962     This->dirty = FALSE;
963     This->highestProp = 0;
964     dictionary_destroy(This->name_to_propid);
965     This->name_to_propid = NULL;
966     dictionary_destroy(This->propid_to_name);
967     This->propid_to_name = NULL;
968     hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
969     if (FAILED(hr))
970         goto end;
971     if (stat.cbSize.u.HighPart)
972     {
973         WARN("stream too big\n");
974         /* maximum size varies, but it can't be this big */
975         hr = STG_E_INVALIDHEADER;
976         goto end;
977     }
978     if (stat.cbSize.u.LowPart == 0)
979     {
980         /* empty stream is okay, we might be being called from Create */
981         hr = S_OK;
982         goto end;
983     }
984     else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
985      sizeof(FORMATIDOFFSET))
986     {
987         WARN("stream too small\n");
988         hr = STG_E_INVALIDHEADER;
989         goto end;
990     }
991     hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
992     /* I've only seen reserved == 1, but the article says I shouldn't disallow
993      * higher values.
994      */
995     if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
996     {
997         WARN("bad magic in prop set header\n");
998         hr = STG_E_INVALIDHEADER;
999         goto end;
1000     }
1001     if (hdr.wFormat != 0 && hdr.wFormat != 1)
1002     {
1003         WARN("bad format version %d\n", hdr.wFormat);
1004         hr = STG_E_INVALIDHEADER;
1005         goto end;
1006     }
1007     memcpy(&This->clsid, &hdr.clsid, sizeof(This->clsid));
1008     This->originatorOS = hdr.dwOSVer;
1009     if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1010         WARN("File comes from a Mac, strings will probably be screwed up\n");
1011     hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1012     if (FAILED(hr))
1013         goto end;
1014     if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
1015     {
1016         WARN("invalid offset %ld (stream length is %ld)\n", fmtOffset.dwOffset,
1017          stat.cbSize.u.LowPart);
1018         hr = STG_E_INVALIDHEADER;
1019         goto end;
1020     }
1021     /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1022      * follow not one, but two sections.  The first is the standard properties
1023      * for the document summary information, and the second is user-defined
1024      * properties.  This is the only case in which multiple sections are
1025      * allowed.
1026      * Reading the second stream isn't implemented yet.
1027      */
1028     hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, &sectionHdr);
1029     if (FAILED(hr))
1030         goto end;
1031     /* The section size includes the section header, so check it */
1032     if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1033     {
1034         WARN("section header too small, got %ld, expected at least %d\n",
1035          sectionHdr.cbSection, sizeof(PROPERTYSECTIONHEADER));
1036         hr = STG_E_INVALIDHEADER;
1037         goto end;
1038     }
1039     buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1040      sizeof(PROPERTYSECTIONHEADER));
1041     if (!buf)
1042     {
1043         hr = STG_E_INSUFFICIENTMEMORY;
1044         goto end;
1045     }
1046     hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
1047      sizeof(PROPERTYSECTIONHEADER), &count);
1048     if (FAILED(hr))
1049         goto end;
1050     dictionary_destroy(This->propid_to_prop);
1051     This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
1052      PropertyStorage_PropertyDestroy, This);
1053     for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1054     {
1055         PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
1056          i * sizeof(PROPERTYIDOFFSET));
1057
1058         if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1059          idOffset->dwOffset >= sectionHdr.cbSection - sizeof(DWORD))
1060             hr = STG_E_INVALIDPOINTER;
1061         else
1062         {
1063             if (idOffset->propid >= PID_FIRST_USABLE &&
1064              idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1065              This->highestProp)
1066                 This->highestProp = idOffset->propid;
1067             if (idOffset->propid == 0)
1068             {
1069                 /* Don't read the dictionary yet, its entries depend on the
1070                  * code page.  Just store the offset so we know to read it
1071                  * later.
1072                  */
1073                 dictOffset = idOffset->dwOffset;
1074             }
1075             else
1076             {
1077                 DWORD type;
1078                 PROPVARIANT prop;
1079
1080                 StorageUtl_ReadDWord(buf,
1081                  idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER), &type);
1082                 if (SUCCEEDED(PropertyStorage_ReadProperty(&prop, type,
1083                  buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER) +
1084                  sizeof(DWORD))))
1085                 {
1086                     /* FIXME: the PID_CODEPAGE and PID_LOCALE special cases
1087                      * aren't really needed, just look them up in
1088                      * propid_to_prop when needed
1089                      */
1090                     switch(idOffset->propid)
1091                     {
1092                     case PID_CODEPAGE:
1093                         if (prop.vt == VT_I2)
1094                             This->codePage = (UINT)prop.u.iVal;
1095                         break;
1096                     case PID_LOCALE:
1097                         if (prop.vt == VT_I4)
1098                             This->locale = (LCID)prop.u.lVal;
1099                         break;
1100                     case PID_BEHAVIOR:
1101                         if (prop.vt == VT_I4 && prop.u.lVal)
1102                             This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1103                         break;
1104                     default:
1105                         hr = PropertyStorage_StorePropWithId(This,
1106                          idOffset->propid, &prop);
1107                     }
1108                 }
1109             }
1110         }
1111     }
1112     if (!This->codePage)
1113     {
1114         /* default to Unicode unless told not to, as specified here:
1115          * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
1116          */
1117         if (This->grfFlags & PROPSETFLAG_ANSI)
1118             This->codePage = GetACP();
1119         else
1120             This->codePage = 1200;
1121     }
1122     if (!This->locale)
1123         This->locale = LOCALE_SYSTEM_DEFAULT;
1124     TRACE("Code page is %d, locale is %ld\n", This->codePage, This->locale);
1125     if (dictOffset)
1126         PropertyStorage_ReadDictionary(This,
1127          buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
1128
1129 end:
1130     HeapFree(GetProcessHeap(), 0, buf);
1131     return hr;
1132 }
1133
1134
1135 static HRESULT PropertyStorage_WriteToStream(IPropertyStorage *iface)
1136 {
1137     FIXME("\n");
1138     return E_NOTIMPL;
1139 }
1140
1141 /***********************************************************************
1142  * PropertyStorage_Construct
1143  */
1144 static HRESULT PropertyStorage_Construct(IStream *stm, REFFMTID rfmtid,
1145  DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
1146 {
1147     PropertyStorage_impl *ps;
1148     HRESULT hr;
1149
1150     ps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *ps);
1151     if (!ps)
1152         return E_OUTOFMEMORY;
1153
1154     ps->vtbl = &IPropertyStorage_Vtbl;
1155     ps->ref = 1;
1156     InitializeCriticalSection(&ps->cs);
1157     ps->stm = stm;
1158     memcpy(&ps->fmtid, rfmtid, sizeof(ps->fmtid));
1159     ps->grfFlags = grfFlags;
1160     ps->grfMode = grfMode;
1161
1162     hr = PropertyStorage_ReadFromStream((IPropertyStorage *)ps);
1163     if (SUCCEEDED(hr))
1164     {
1165         *pps = (IPropertyStorage *)ps;
1166         TRACE("PropertyStorage %p constructed\n", ps);
1167         hr = S_OK;
1168     }
1169     else
1170         HeapFree(GetProcessHeap(), 0, ps);
1171     return hr;
1172 }
1173
1174
1175 /***********************************************************************
1176  * Implementation of IPropertySetStorage
1177  */
1178
1179 #define BITS_PER_BYTE    8
1180 #define CHARMASK         0x1f
1181 #define BITS_IN_CHARMASK 5
1182
1183 /* Converts rfmtid to a string and returns the resulting string.  If rfmtid
1184  * is a well-known FMTID, it just returns a static string.  Otherwise it
1185  * creates the appropriate string name in str, which must be 27 characters
1186  * in length, and returns str.
1187  * Based on the algorithm described here:
1188  * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
1189  */
1190 static LPCWSTR format_id_to_name(REFFMTID rfmtid, LPWSTR str)
1191 {
1192     static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
1193     static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
1194         'I','n','f','o','r','m','a','t','i','o','n',0 };
1195     static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
1196         'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',
1197         0 };
1198     LPCWSTR ret;
1199
1200     if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
1201         ret = szSummaryInfo;
1202     else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
1203         ret = szDocSummaryInfo;
1204     else
1205     {
1206         BYTE *fmtptr;
1207         WCHAR *pstr = str;
1208         ULONG bitsRemaining = BITS_PER_BYTE;
1209
1210         *pstr++ = 5;
1211         for (fmtptr = (BYTE *)rfmtid; fmtptr < (BYTE *)rfmtid + sizeof(FMTID); )
1212         {
1213             ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
1214
1215             if (bitsRemaining >= BITS_IN_CHARMASK)
1216             {
1217                 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
1218                 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
1219                  *pstr <= 'z')
1220                     *pstr += 'A' - 'a';
1221                 pstr++;
1222                 bitsRemaining -= BITS_IN_CHARMASK;
1223                 if (bitsRemaining == 0)
1224                 {
1225                     fmtptr++;
1226                     bitsRemaining = BITS_PER_BYTE;
1227                 }
1228             }
1229             else
1230             {
1231                 if (++fmtptr < (BYTE *)rfmtid + sizeof(FMTID))
1232                     i |= *fmtptr << bitsRemaining;
1233                 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
1234             }
1235         }
1236         *pstr = 0;
1237         ret = str;
1238     }
1239     TRACE("returning %s\n", debugstr_w(ret));
1240     return ret;
1241 }
1242
1243 /************************************************************************
1244  * IPropertySetStorage_fnQueryInterface (IUnknown)
1245  *
1246  *  This method forwards to the common QueryInterface implementation
1247  */
1248 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
1249     IPropertySetStorage *ppstg,
1250     REFIID riid,
1251     void** ppvObject)
1252 {
1253     _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1254     return StorageBaseImpl_QueryInterface( (IStorage*)This, riid, ppvObject );
1255 }
1256
1257 /************************************************************************
1258  * IPropertySetStorage_fnAddRef (IUnknown)
1259  *
1260  *  This method forwards to the common AddRef implementation
1261  */
1262 static ULONG WINAPI IPropertySetStorage_fnAddRef(
1263     IPropertySetStorage *ppstg)
1264 {
1265     _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1266     return StorageBaseImpl_AddRef( (IStorage*)This );
1267 }
1268
1269 /************************************************************************
1270  * IPropertySetStorage_fnRelease (IUnknown)
1271  *
1272  *  This method forwards to the common Release implementation
1273  */
1274 static ULONG WINAPI IPropertySetStorage_fnRelease(
1275     IPropertySetStorage *ppstg)
1276 {
1277     _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1278     return StorageBaseImpl_Release( (IStorage*)This );
1279 }
1280
1281 /************************************************************************
1282  * IPropertySetStorage_fnCreate (IPropertySetStorage)
1283  */
1284 static HRESULT WINAPI IPropertySetStorage_fnCreate(
1285     IPropertySetStorage *ppstg,
1286     REFFMTID rfmtid,
1287     const CLSID* pclsid,
1288     DWORD grfFlags,
1289     DWORD grfMode,
1290     IPropertyStorage** ppprstg)
1291 {
1292     _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1293     WCHAR nameBuf[27];
1294     LPCWSTR name = NULL;
1295     IStream *stm = NULL;
1296     HRESULT r;
1297
1298     TRACE("%p %s %08lx %08lx %p\n", This, debugstr_guid(rfmtid), grfFlags,
1299      grfMode, ppprstg);
1300
1301     /* be picky */
1302     if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
1303     {
1304         r = STG_E_INVALIDFLAG;
1305         goto end;
1306     }
1307
1308     if (!rfmtid)
1309     {
1310         r = E_INVALIDARG;
1311         goto end;
1312     }
1313
1314     /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
1315      * storage, not a stream.  For now, disallow it.
1316      */
1317     if (grfFlags & PROPSETFLAG_NONSIMPLE)
1318     {
1319         FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
1320         r = STG_E_INVALIDFLAG;
1321         goto end;
1322     }
1323
1324     name = format_id_to_name(rfmtid, nameBuf);
1325
1326     r = IStorage_CreateStream( (IStorage*)This, name, grfMode, 0, 0, &stm );
1327     if (FAILED(r))
1328         goto end;
1329
1330     r = PropertyStorage_Construct(stm, rfmtid, grfFlags, grfMode, ppprstg);
1331
1332 end:
1333     TRACE("returning 0x%08lx\n", r);
1334     return r;
1335 }
1336
1337 /************************************************************************
1338  * IPropertySetStorage_fnOpen (IPropertySetStorage)
1339  */
1340 static HRESULT WINAPI IPropertySetStorage_fnOpen(
1341     IPropertySetStorage *ppstg,
1342     REFFMTID rfmtid,
1343     DWORD grfMode,
1344     IPropertyStorage** ppprstg)
1345 {
1346     _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1347     IStream *stm = NULL;
1348     WCHAR nameBuf[27];
1349     LPCWSTR name = NULL;
1350     HRESULT r;
1351
1352     TRACE("%p %s %08lx %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
1353
1354     /* be picky */
1355     if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
1356         grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
1357     {
1358         r = STG_E_INVALIDFLAG;
1359         goto end;
1360     }
1361
1362     if (!rfmtid)
1363     {
1364         r = E_INVALIDARG;
1365         goto end;
1366     }
1367
1368     name = format_id_to_name(rfmtid, nameBuf);
1369
1370     r = IStorage_OpenStream((IStorage*) This, name, 0, grfMode, 0, &stm );
1371     if (FAILED(r))
1372         goto end;
1373
1374     r = PropertyStorage_Construct(stm, rfmtid, PROPSETFLAG_DEFAULT, grfMode,
1375      ppprstg);
1376
1377 end:
1378     TRACE("returning 0x%08lx\n", r);
1379     return r;
1380 }
1381
1382 /************************************************************************
1383  * IPropertySetStorage_fnDelete (IPropertySetStorage)
1384  */
1385 static HRESULT WINAPI IPropertySetStorage_fnDelete(
1386     IPropertySetStorage *ppstg,
1387     REFFMTID rfmtid)
1388 {
1389     _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1390     IStorage *stg = NULL;
1391     WCHAR nameBuf[27];
1392     LPCWSTR name = NULL;
1393
1394     TRACE("%p %s\n", This, debugstr_guid(rfmtid));
1395
1396     if (!rfmtid)
1397         return E_INVALIDARG;
1398
1399     name = format_id_to_name(rfmtid, nameBuf);
1400     if (!name)
1401         return STG_E_FILENOTFOUND;
1402
1403     stg = (IStorage*) This;
1404     return IStorage_DestroyElement(stg, name);
1405 }
1406
1407 /************************************************************************
1408  * IPropertySetStorage_fnEnum (IPropertySetStorage)
1409  */
1410 static HRESULT WINAPI IPropertySetStorage_fnEnum(
1411     IPropertySetStorage *ppstg,
1412     IEnumSTATPROPSETSTG** ppenum)
1413 {
1414     _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1415     FIXME("%p\n", This);
1416     return E_NOTIMPL;
1417 }
1418
1419
1420 /***********************************************************************
1421  * vtables
1422  */
1423 IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
1424 {
1425     IPropertySetStorage_fnQueryInterface,
1426     IPropertySetStorage_fnAddRef,
1427     IPropertySetStorage_fnRelease,
1428     IPropertySetStorage_fnCreate,
1429     IPropertySetStorage_fnOpen,
1430     IPropertySetStorage_fnDelete,
1431     IPropertySetStorage_fnEnum
1432 };
1433
1434 static IPropertyStorageVtbl IPropertyStorage_Vtbl =
1435 {
1436     IPropertyStorage_fnQueryInterface,
1437     IPropertyStorage_fnAddRef,
1438     IPropertyStorage_fnRelease,
1439     IPropertyStorage_fnReadMultiple,
1440     IPropertyStorage_fnWriteMultiple,
1441     IPropertyStorage_fnDeleteMultiple,
1442     IPropertyStorage_fnReadPropertyNames,
1443     IPropertyStorage_fnWritePropertyNames,
1444     IPropertyStorage_fnDeletePropertyNames,
1445     IPropertyStorage_fnCommit,
1446     IPropertyStorage_fnRevert,
1447     IPropertyStorage_fnEnum,
1448     IPropertyStorage_fnSetTimes,
1449     IPropertyStorage_fnSetClass,
1450     IPropertyStorage_fnStat,
1451 };