mmdevapi: Fix IsFormatSupported and initialization.
[wine] / dlls / mapi32 / prop.c
1 /*
2  * Property functions
3  *
4  * Copyright 2004 Jon Griffiths
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22 #define NONAMELESSUNION
23 #define NONAMELESSSTRUCT
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winreg.h"
27 #include "winerror.h"
28 #include "winternl.h"
29 #include "objbase.h"
30 #include "shlwapi.h"
31 #include "wine/list.h"
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
34 #include "mapival.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(mapi);
37
38 BOOL WINAPI FBadRglpszA(LPSTR*,ULONG);
39
40 /* Internal: Check if a property value array is invalid */
41 static inline ULONG PROP_BadArray(LPSPropValue lpProp, size_t elemSize)
42 {
43     return IsBadReadPtr(lpProp->Value.MVi.lpi, lpProp->Value.MVi.cValues * elemSize);
44 }
45
46 /*************************************************************************
47  * PropCopyMore@16 (MAPI32.76)
48  *
49  * Copy a property value.
50  *
51  * PARAMS
52  *  lpDest [O] Destination for the copied value
53  *  lpSrc  [I] Property value to copy to lpDest
54  *  lpMore [I] Linked memory allocation function (pass MAPIAllocateMore())
55  *  lpOrig [I] Original allocation to which memory will be linked
56  *
57  * RETURNS
58  *  Success: S_OK. lpDest contains a deep copy of lpSrc.
59  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid,
60  *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails.
61  *
62  * NOTES
63  *  Any elements within the property returned should not be individually
64  *  freed, as they will be freed when lpOrig is.
65  */
66 SCODE WINAPI PropCopyMore(LPSPropValue lpDest, LPSPropValue lpSrc,
67                           ALLOCATEMORE *lpMore, LPVOID lpOrig)
68 {
69     ULONG ulLen, i;
70     SCODE scode = S_OK;
71
72     TRACE("(%p,%p,%p,%p)\n", lpDest, lpSrc, lpMore, lpOrig);
73
74     if (!lpDest || IsBadWritePtr(lpDest, sizeof(SPropValue)) ||
75         FBadProp(lpSrc) || !lpMore)
76         return MAPI_E_INVALID_PARAMETER;
77
78     /* Shallow copy first, this is sufficient for properties without pointers */
79     *lpDest = *lpSrc;
80
81    switch (PROP_TYPE(lpSrc->ulPropTag))
82     {
83     case PT_CLSID:
84         scode = lpMore(sizeof(GUID), lpOrig, (LPVOID*)&lpDest->Value.lpguid);
85         if (SUCCEEDED(scode))
86             *lpDest->Value.lpguid = *lpSrc->Value.lpguid;
87         break;
88     case PT_STRING8:
89         ulLen = lstrlenA(lpSrc->Value.lpszA) + 1u;
90         scode = lpMore(ulLen, lpOrig, (LPVOID*)&lpDest->Value.lpszA);
91         if (SUCCEEDED(scode))
92             memcpy(lpDest->Value.lpszA, lpSrc->Value.lpszA, ulLen);
93         break;
94     case PT_UNICODE:
95         ulLen = (strlenW(lpSrc->Value.lpszW) + 1u) * sizeof(WCHAR);
96         scode = lpMore(ulLen, lpOrig, (LPVOID*)&lpDest->Value.lpszW);
97         if (SUCCEEDED(scode))
98             memcpy(lpDest->Value.lpszW, lpSrc->Value.lpszW, ulLen);
99         break;
100     case PT_BINARY:
101         scode = lpMore(lpSrc->Value.bin.cb, lpOrig, (LPVOID*)&lpDest->Value.bin.lpb);
102         if (SUCCEEDED(scode))
103             memcpy(lpDest->Value.bin.lpb, lpSrc->Value.bin.lpb, lpSrc->Value.bin.cb);
104         break;
105     default:
106         if (lpSrc->ulPropTag & MV_FLAG)
107         {
108             ulLen = UlPropSize(lpSrc);
109
110             if (PROP_TYPE(lpSrc->ulPropTag) == PT_MV_STRING8 ||
111                 PROP_TYPE(lpSrc->ulPropTag) == PT_MV_UNICODE)
112             {
113                 /* UlPropSize doesn't account for the string pointers */
114                 ulLen += lpSrc->Value.MVszA.cValues * sizeof(char*);
115             }
116             else if (PROP_TYPE(lpSrc->ulPropTag) == PT_MV_BINARY)
117             {
118                /* UlPropSize doesn't account for the SBinary structs */
119                ulLen += lpSrc->Value.MVbin.cValues * sizeof(SBinary);
120             }
121
122             lpDest->Value.MVi.cValues = lpSrc->Value.MVi.cValues;
123             scode = lpMore(ulLen, lpOrig, (LPVOID*)&lpDest->Value.MVi.lpi);
124             if (FAILED(scode))
125                 break;
126
127             /* Note that we could allocate the memory for each value in a
128              * multi-value property separately, however if an allocation failed
129              * we would be left with a bunch of allocated memory, which (while
130              * not really leaked) is unusable until lpOrig is freed. So for
131              * strings and binary arrays we make a single allocation for all
132              * of the data. This is consistent since individual elements can't
133              * be freed anyway.
134              */
135
136             switch (PROP_TYPE(lpSrc->ulPropTag))
137             {
138             case PT_MV_STRING8:
139             {
140                 char *lpNextStr = (char*)(lpDest->Value.MVszA.lppszA +
141                                           lpDest->Value.MVszA.cValues);
142
143                 for (i = 0; i < lpSrc->Value.MVszA.cValues; i++)
144                 {
145                     ULONG ulStrLen = lstrlenA(lpSrc->Value.MVszA.lppszA[i]) + 1u;
146
147                     lpDest->Value.MVszA.lppszA[i] = lpNextStr;
148                     memcpy(lpNextStr, lpSrc->Value.MVszA.lppszA[i], ulStrLen);
149                     lpNextStr += ulStrLen;
150                 }
151                 break;
152             }
153             case PT_MV_UNICODE:
154             {
155                 WCHAR *lpNextStr = (WCHAR*)(lpDest->Value.MVszW.lppszW +
156                                             lpDest->Value.MVszW.cValues);
157
158                 for (i = 0; i < lpSrc->Value.MVszW.cValues; i++)
159                 {
160                     ULONG ulStrLen = strlenW(lpSrc->Value.MVszW.lppszW[i]) + 1u;
161
162                     lpDest->Value.MVszW.lppszW[i] = lpNextStr;
163                     memcpy(lpNextStr, lpSrc->Value.MVszW.lppszW[i], ulStrLen * sizeof(WCHAR));
164                     lpNextStr += ulStrLen;
165                 }
166                 break;
167             }
168             case PT_MV_BINARY:
169             {
170                 LPBYTE lpNext = (LPBYTE)(lpDest->Value.MVbin.lpbin +
171                                          lpDest->Value.MVbin.cValues);
172
173                 for (i = 0; i < lpSrc->Value.MVszW.cValues; i++)
174                 {
175                     lpDest->Value.MVbin.lpbin[i].cb = lpSrc->Value.MVbin.lpbin[i].cb;
176                     lpDest->Value.MVbin.lpbin[i].lpb = lpNext;
177                     memcpy(lpNext, lpSrc->Value.MVbin.lpbin[i].lpb, lpDest->Value.MVbin.lpbin[i].cb);
178                     lpNext += lpDest->Value.MVbin.lpbin[i].cb;
179                 }
180                 break;
181             }
182             default:
183                 /* No embedded pointers, just copy the data over */
184                 memcpy(lpDest->Value.MVi.lpi, lpSrc->Value.MVi.lpi, ulLen);
185                 break;
186             }
187             break;
188         }
189     }
190     return scode;
191 }
192
193 /*************************************************************************
194  * UlPropSize@4 (MAPI32.77)
195  *
196  * Determine the size of a property in bytes.
197  *
198  * PARAMS
199  *  lpProp [I] Property to determine the size of
200  *
201  * RETURNS
202  *  Success: The size of the value in lpProp.
203  *  Failure: 0, if a multi-value (array) property is invalid or the type of lpProp
204  *           is unknown.
205  *
206  * NOTES
207  *  - The size returned does not include the size of the SPropValue struct
208  *    or the size of the array of pointers for multi-valued properties that
209  *    contain pointers (such as PT_MV_STRING8 or PT-MV_UNICODE).
210  *  - MSDN incorrectly states that this function returns MAPI_E_CALL_FAILED if
211  *    lpProp is invalid. In reality no checking is performed and this function
212  *    will crash if passed an invalid property, or return 0 if the property
213  *    type is PT_OBJECT or is unknown.
214  */
215 ULONG WINAPI UlPropSize(LPSPropValue lpProp)
216 {
217     ULONG ulRet = 1u, i;
218
219     TRACE("(%p)\n", lpProp);
220
221     switch (PROP_TYPE(lpProp->ulPropTag))
222     {
223     case PT_MV_I2:       ulRet = lpProp->Value.MVi.cValues;
224     case PT_BOOLEAN:
225     case PT_I2:          ulRet *= sizeof(USHORT);
226                          break;
227     case PT_MV_I4:       ulRet = lpProp->Value.MVl.cValues;
228     case PT_ERROR:
229     case PT_I4:          ulRet *= sizeof(LONG);
230                          break;
231     case PT_MV_I8:       ulRet = lpProp->Value.MVli.cValues;
232     case PT_I8:          ulRet *= sizeof(LONG64);
233                          break;
234     case PT_MV_R4:       ulRet = lpProp->Value.MVflt.cValues;
235     case PT_R4:          ulRet *= sizeof(float);
236                          break;
237     case PT_MV_APPTIME:
238     case PT_MV_R8:       ulRet = lpProp->Value.MVdbl.cValues;
239     case PT_APPTIME:
240     case PT_R8:          ulRet *= sizeof(double);
241                          break;
242     case PT_MV_CURRENCY: ulRet = lpProp->Value.MVcur.cValues;
243     case PT_CURRENCY:    ulRet *= sizeof(CY);
244                          break;
245     case PT_MV_SYSTIME:  ulRet = lpProp->Value.MVft.cValues;
246     case PT_SYSTIME:     ulRet *= sizeof(FILETIME);
247                          break;
248     case PT_MV_CLSID:    ulRet = lpProp->Value.MVguid.cValues;
249     case PT_CLSID:       ulRet *= sizeof(GUID);
250                          break;
251     case PT_MV_STRING8:  ulRet = 0u;
252                          for (i = 0; i < lpProp->Value.MVszA.cValues; i++)
253                              ulRet += (lstrlenA(lpProp->Value.MVszA.lppszA[i]) + 1u);
254                          break;
255     case PT_STRING8:     ulRet = lstrlenA(lpProp->Value.lpszA) + 1u;
256                          break;
257     case PT_MV_UNICODE:  ulRet = 0u;
258                          for (i = 0; i < lpProp->Value.MVszW.cValues; i++)
259                              ulRet += (strlenW(lpProp->Value.MVszW.lppszW[i]) + 1u);
260                          ulRet *= sizeof(WCHAR);
261                          break;
262     case PT_UNICODE:     ulRet = (lstrlenW(lpProp->Value.lpszW) + 1u) * sizeof(WCHAR);
263                          break;
264     case PT_MV_BINARY:   ulRet = 0u;
265                          for (i = 0; i < lpProp->Value.MVbin.cValues; i++)
266                              ulRet += lpProp->Value.MVbin.lpbin[i].cb;
267                          break;
268     case PT_BINARY:      ulRet = lpProp->Value.bin.cb;
269                          break;
270     case PT_OBJECT:
271     default:             ulRet = 0u;
272                          break;
273     }
274
275     return ulRet;
276 }
277
278 /*************************************************************************
279  * FPropContainsProp@12 (MAPI32.78)
280  *
281  * Find a property with a given property tag in a property array.
282  *
283  * PARAMS
284  *  lpHaystack [I] Property to match to
285  *  lpNeedle   [I] Property to find in lpHaystack
286  *  ulFuzzy    [I] Flags controlling match type and strictness (FL_* flags from "mapidefs.h")
287  *
288  * RETURNS
289  *  TRUE, if lpNeedle matches lpHaystack according to the criteria of ulFuzzy.
290  *
291  * NOTES
292  *  Only property types of PT_STRING8 and PT_BINARY are handled by this function.
293  */
294 BOOL WINAPI FPropContainsProp(LPSPropValue lpHaystack, LPSPropValue lpNeedle, ULONG ulFuzzy)
295 {
296     TRACE("(%p,%p,0x%08x)\n", lpHaystack, lpNeedle, ulFuzzy);
297
298     if (FBadProp(lpHaystack) || FBadProp(lpNeedle) ||
299         PROP_TYPE(lpHaystack->ulPropTag) != PROP_TYPE(lpNeedle->ulPropTag))
300         return FALSE;
301
302     /* FIXME: Do later versions support Unicode as well? */
303
304     if (PROP_TYPE(lpHaystack->ulPropTag) == PT_STRING8)
305     {
306         DWORD dwFlags = 0, dwNeedleLen, dwHaystackLen;
307
308         if (ulFuzzy & FL_IGNORECASE)
309             dwFlags |= NORM_IGNORECASE;
310         if (ulFuzzy & FL_IGNORENONSPACE)
311             dwFlags |= NORM_IGNORENONSPACE;
312         if (ulFuzzy & FL_LOOSE)
313             dwFlags |= (NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS);
314
315         dwNeedleLen = lstrlenA(lpNeedle->Value.lpszA);
316         dwHaystackLen = lstrlenA(lpHaystack->Value.lpszA);
317
318         if ((ulFuzzy & (FL_SUBSTRING|FL_PREFIX)) == FL_PREFIX)
319         {
320             if (dwNeedleLen <= dwHaystackLen &&
321                 CompareStringA(LOCALE_USER_DEFAULT, dwFlags,
322                                lpHaystack->Value.lpszA, dwNeedleLen,
323                                lpNeedle->Value.lpszA, dwNeedleLen) == CSTR_EQUAL)
324                 return TRUE; /* needle is a prefix of haystack */
325         }
326         else if ((ulFuzzy & (FL_SUBSTRING|FL_PREFIX)) == FL_SUBSTRING)
327         {
328             LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD) = StrChrA;
329             LPSTR lpStr = lpHaystack->Value.lpszA;
330
331             if (dwFlags & NORM_IGNORECASE)
332                 pStrChrFn = StrChrIA;
333
334             while ((lpStr = pStrChrFn(lpStr, *lpNeedle->Value.lpszA)) != NULL)
335             {
336                 dwHaystackLen -= (lpStr - lpHaystack->Value.lpszA);
337                 if (dwNeedleLen <= dwHaystackLen &&
338                     CompareStringA(LOCALE_USER_DEFAULT, dwFlags,
339                                lpStr, dwNeedleLen,
340                                lpNeedle->Value.lpszA, dwNeedleLen) == CSTR_EQUAL)
341                     return TRUE; /* needle is a substring of haystack */
342                 lpStr++;
343             }
344         }
345         else if (CompareStringA(LOCALE_USER_DEFAULT, dwFlags,
346                                 lpHaystack->Value.lpszA, dwHaystackLen,
347                                 lpNeedle->Value.lpszA, dwNeedleLen) == CSTR_EQUAL)
348             return TRUE; /* full string match */
349     }
350     else if (PROP_TYPE(lpHaystack->ulPropTag) == PT_BINARY)
351     {
352         if ((ulFuzzy & (FL_SUBSTRING|FL_PREFIX)) == FL_PREFIX)
353         {
354             if (lpNeedle->Value.bin.cb <= lpHaystack->Value.bin.cb &&
355                 !memcmp(lpNeedle->Value.bin.lpb, lpHaystack->Value.bin.lpb,
356                         lpNeedle->Value.bin.cb))
357                 return TRUE; /* needle is a prefix of haystack */
358         }
359         else if ((ulFuzzy & (FL_SUBSTRING|FL_PREFIX)) == FL_SUBSTRING)
360         {
361             ULONG ulLen = lpHaystack->Value.bin.cb;
362             LPBYTE lpb = lpHaystack->Value.bin.lpb;
363
364             while ((lpb = memchr(lpb, *lpNeedle->Value.bin.lpb, ulLen)) != NULL)
365             {
366                 ulLen = lpHaystack->Value.bin.cb - (lpb - lpHaystack->Value.bin.lpb);
367                 if (lpNeedle->Value.bin.cb <= ulLen &&
368                     !memcmp(lpNeedle->Value.bin.lpb, lpb, lpNeedle->Value.bin.cb))
369                     return TRUE; /* needle is a substring of haystack */
370                 lpb++;
371             }
372         }
373         else if (!LPropCompareProp(lpHaystack, lpNeedle))
374             return TRUE; /* needle is an exact match with haystack */
375
376     }
377     return FALSE;
378 }
379
380 /*************************************************************************
381  * FPropCompareProp@12 (MAPI32.79)
382  *
383  * Compare two properties.
384  *
385  * PARAMS
386  *  lpPropLeft  [I] Left hand property to compare to lpPropRight
387  *  ulOp        [I] Comparison operator (RELOP_* enum from "mapidefs.h")
388  *  lpPropRight [I] Right hand property to compare to lpPropLeft
389  *
390  * RETURNS
391  *  TRUE, if the comparison is true, FALSE otherwise.
392  */
393 BOOL WINAPI FPropCompareProp(LPSPropValue lpPropLeft, ULONG ulOp, LPSPropValue lpPropRight)
394 {
395     LONG iCmp;
396
397     TRACE("(%p,%d,%p)\n", lpPropLeft, ulOp, lpPropRight);
398
399     if (ulOp > RELOP_RE || FBadProp(lpPropLeft) || FBadProp(lpPropRight))
400         return FALSE;
401
402     if (ulOp == RELOP_RE)
403     {
404         FIXME("Comparison operator RELOP_RE not yet implemented!\n");
405         return FALSE;
406     }
407
408     iCmp = LPropCompareProp(lpPropLeft, lpPropRight);
409
410     switch (ulOp)
411     {
412     case RELOP_LT: return iCmp <  0 ? TRUE : FALSE;
413     case RELOP_LE: return iCmp <= 0 ? TRUE : FALSE;
414     case RELOP_GT: return iCmp >  0 ? TRUE : FALSE;
415     case RELOP_GE: return iCmp >= 0 ? TRUE : FALSE;
416     case RELOP_EQ: return iCmp == 0 ? TRUE : FALSE;
417     case RELOP_NE: return iCmp != 0 ? TRUE : FALSE;
418     }
419     return FALSE;
420 }
421
422 /*************************************************************************
423  * LPropCompareProp@8 (MAPI32.80)
424  *
425  * Compare two properties.
426  *
427  * PARAMS
428  *  lpPropLeft  [I] Left hand property to compare to lpPropRight
429  *  lpPropRight [I] Right hand property to compare to lpPropLeft
430  *
431  * RETURNS
432  *  An integer less than, equal to or greater than 0, indicating that
433  *  lpszStr is less than, the same, or greater than lpszComp.
434  */
435 LONG WINAPI LPropCompareProp(LPSPropValue lpPropLeft, LPSPropValue lpPropRight)
436 {
437     LONG iRet;
438
439     TRACE("(%p->0x%08x,%p->0x%08x)\n", lpPropLeft, lpPropLeft->ulPropTag,
440           lpPropRight, lpPropRight->ulPropTag);
441
442     /* If the properties are not the same, sort by property type */
443     if (PROP_TYPE(lpPropLeft->ulPropTag) != PROP_TYPE(lpPropRight->ulPropTag))
444         return (LONG)PROP_TYPE(lpPropLeft->ulPropTag) - (LONG)PROP_TYPE(lpPropRight->ulPropTag);
445
446     switch (PROP_TYPE(lpPropLeft->ulPropTag))
447     {
448     case PT_UNSPECIFIED:
449     case PT_NULL:
450         return 0; /* NULLs are equal */
451     case PT_I2:
452         return lpPropLeft->Value.i - lpPropRight->Value.i;
453     case PT_I4:
454         return lpPropLeft->Value.l - lpPropRight->Value.l;
455     case PT_I8:
456         if (lpPropLeft->Value.li.QuadPart > lpPropRight->Value.li.QuadPart)
457             return 1;
458         if (lpPropLeft->Value.li.QuadPart == lpPropRight->Value.li.QuadPart)
459             return 0;
460         return -1;
461     case PT_R4:
462         if (lpPropLeft->Value.flt > lpPropRight->Value.flt)
463             return 1;
464         if (lpPropLeft->Value.flt == lpPropRight->Value.flt)
465             return 0;
466         return -1;
467     case PT_APPTIME:
468     case PT_R8:
469         if (lpPropLeft->Value.dbl > lpPropRight->Value.dbl)
470             return 1;
471         if (lpPropLeft->Value.dbl == lpPropRight->Value.dbl)
472             return 0;
473         return -1;
474     case PT_CURRENCY:
475         if (lpPropLeft->Value.cur.int64 > lpPropRight->Value.cur.int64)
476             return 1;
477         if (lpPropLeft->Value.cur.int64 == lpPropRight->Value.cur.int64)
478             return 0;
479         return -1;
480     case PT_SYSTIME:
481         return CompareFileTime(&lpPropLeft->Value.ft, &lpPropRight->Value.ft);
482     case PT_BOOLEAN:
483         return (lpPropLeft->Value.b ? 1 : 0) - (lpPropRight->Value.b ? 1 : 0);
484     case PT_BINARY:
485         if (lpPropLeft->Value.bin.cb == lpPropRight->Value.bin.cb)
486             iRet = memcmp(lpPropLeft->Value.bin.lpb, lpPropRight->Value.bin.lpb,
487                           lpPropLeft->Value.bin.cb);
488         else
489         {
490             iRet = memcmp(lpPropLeft->Value.bin.lpb, lpPropRight->Value.bin.lpb,
491                           min(lpPropLeft->Value.bin.cb, lpPropRight->Value.bin.cb));
492
493             if (!iRet)
494                 iRet = lpPropLeft->Value.bin.cb - lpPropRight->Value.bin.cb;
495         }
496         return iRet;
497     case PT_STRING8:
498         return lstrcmpA(lpPropLeft->Value.lpszA, lpPropRight->Value.lpszA);
499     case PT_UNICODE:
500         return strcmpW(lpPropLeft->Value.lpszW, lpPropRight->Value.lpszW);
501     case PT_ERROR:
502         if (lpPropLeft->Value.err > lpPropRight->Value.err)
503             return 1;
504         if (lpPropLeft->Value.err == lpPropRight->Value.err)
505             return 0;
506         return -1;
507     case PT_CLSID:
508         return memcmp(lpPropLeft->Value.lpguid, lpPropRight->Value.lpguid,
509                       sizeof(GUID));
510     }
511     FIXME("Unhandled property type %d\n", PROP_TYPE(lpPropLeft->ulPropTag));
512     return 0;
513 }
514
515 /*************************************************************************
516  * HrGetOneProp@8 (MAPI32.135)
517  *
518  * Get a property value from an IMAPIProp object.
519  *
520  * PARAMS
521  *  lpIProp   [I] IMAPIProp object to get the property value in
522  *  ulPropTag [I] Property tag of the property to get
523  *  lppProp   [O] Destination for the returned property
524  *
525  * RETURNS
526  *  Success: S_OK. *lppProp contains the property value requested.
527  *  Failure: MAPI_E_NOT_FOUND, if no property value has the tag given by ulPropTag.
528  */
529 HRESULT WINAPI HrGetOneProp(LPMAPIPROP lpIProp, ULONG ulPropTag, LPSPropValue *lppProp)
530 {
531     SPropTagArray pta;
532     ULONG ulCount;
533     HRESULT hRet;
534
535     TRACE("(%p,%d,%p)\n", lpIProp, ulPropTag, lppProp);
536
537     pta.cValues = 1u;
538     pta.aulPropTag[0] = ulPropTag;
539     hRet = IMAPIProp_GetProps(lpIProp, &pta, 0u, &ulCount, lppProp);
540     if (hRet == MAPI_W_ERRORS_RETURNED)
541     {
542         MAPIFreeBuffer(*lppProp);
543         *lppProp = NULL;
544         hRet = MAPI_E_NOT_FOUND;
545     }
546     return hRet;
547 }
548
549 /*************************************************************************
550  * HrSetOneProp@8 (MAPI32.136)
551  *
552  * Set a property value in an IMAPIProp object.
553  *
554  * PARAMS
555  *  lpIProp [I] IMAPIProp object to set the property value in
556  *  lpProp  [I] Property value to set
557  *
558  * RETURNS
559  *  Success: S_OK. The value in lpProp is set in lpIProp.
560  *  Failure: An error result from IMAPIProp_SetProps().
561  */
562 HRESULT WINAPI HrSetOneProp(LPMAPIPROP lpIProp, LPSPropValue lpProp)
563 {
564     TRACE("(%p,%p)\n", lpIProp, lpProp);
565
566     return IMAPIProp_SetProps(lpIProp, 1u, lpProp, NULL);
567 }
568
569 /*************************************************************************
570  * FPropExists@8 (MAPI32.137)
571  *
572  * Find a property with a given property tag in an IMAPIProp object.
573  *
574  * PARAMS
575  *  lpIProp   [I] IMAPIProp object to find the property tag in
576  *  ulPropTag [I] Property tag to find
577  *
578  * RETURNS
579  *  TRUE, if ulPropTag matches a property held in lpIProp,
580  *  FALSE, otherwise.
581  *
582  * NOTES
583  *  if ulPropTag has a property type of PT_UNSPECIFIED, then only the property
584  *  Ids need to match for a successful match to occur.
585  */
586  BOOL WINAPI FPropExists(LPMAPIPROP lpIProp, ULONG ulPropTag)
587  {
588     BOOL bRet = FALSE;
589
590     TRACE("(%p,%d)\n", lpIProp, ulPropTag);
591
592     if (lpIProp)
593     {
594         LPSPropTagArray lpTags;
595         ULONG i;
596
597         if (FAILED(IMAPIProp_GetPropList(lpIProp, 0u, &lpTags)))
598             return FALSE;
599
600         for (i = 0; i < lpTags->cValues; i++)
601         {
602             if (!FBadPropTag(lpTags->aulPropTag[i]) &&
603                 (lpTags->aulPropTag[i] == ulPropTag ||
604                  (PROP_TYPE(ulPropTag) == PT_UNSPECIFIED &&
605                   PROP_ID(lpTags->aulPropTag[i]) == lpTags->aulPropTag[i])))
606             {
607                 bRet = TRUE;
608                 break;
609             }
610         }
611         MAPIFreeBuffer(lpTags);
612     }
613     return bRet;
614 }
615
616 /*************************************************************************
617  * PpropFindProp@12 (MAPI32.138)
618  *
619  * Find a property with a given property tag in a property array.
620  *
621  * PARAMS
622  *  lpProps   [I] Property array to search
623  *  cValues   [I] Number of properties in lpProps
624  *  ulPropTag [I] Property tag to find
625  *
626  * RETURNS
627  *  A pointer to the matching property, or NULL if none was found.
628  *
629  * NOTES
630  *  if ulPropTag has a property type of PT_UNSPECIFIED, then only the property
631  *  Ids need to match for a successful match to occur.
632  */
633 LPSPropValue WINAPI PpropFindProp(LPSPropValue lpProps, ULONG cValues, ULONG ulPropTag)
634 {
635     TRACE("(%p,%d,%d)\n", lpProps, cValues, ulPropTag);
636
637     if (lpProps && cValues)
638     {
639         ULONG i;
640         for (i = 0; i < cValues; i++)
641         {
642             if (!FBadPropTag(lpProps[i].ulPropTag) &&
643                 (lpProps[i].ulPropTag == ulPropTag ||
644                  (PROP_TYPE(ulPropTag) == PT_UNSPECIFIED &&
645                   PROP_ID(lpProps[i].ulPropTag) == PROP_ID(ulPropTag))))
646                 return &lpProps[i];
647         }
648     }
649     return NULL;
650 }
651
652 /*************************************************************************
653  * FreePadrlist@4 (MAPI32.139)
654  *
655  * Free the memory used by an address book list.
656  *
657  * PARAMS
658  *  lpAddrs [I] Address book list to free
659  *
660  * RETURNS
661  *  Nothing.
662  */
663 VOID WINAPI FreePadrlist(LPADRLIST lpAddrs)
664 {
665     TRACE("(%p)\n", lpAddrs);
666
667     /* Structures are binary compatible; use the same implementation */
668     FreeProws((LPSRowSet)lpAddrs);
669 }
670
671 /*************************************************************************
672  * FreeProws@4 (MAPI32.140)
673  *
674  * Free the memory used by a row set.
675  *
676  * PARAMS
677  *  lpRowSet [I] Row set to free
678  *
679  * RETURNS
680  *  Nothing.
681  */
682 VOID WINAPI FreeProws(LPSRowSet lpRowSet)
683 {
684     TRACE("(%p)\n", lpRowSet);
685
686     if (lpRowSet)
687     {
688         ULONG i;
689
690         for (i = 0; i < lpRowSet->cRows; i++)
691             MAPIFreeBuffer(lpRowSet->aRow[i].lpProps);
692
693         MAPIFreeBuffer(lpRowSet);
694     }
695 }
696
697 /*************************************************************************
698  * ScCountProps@12 (MAPI32.170)
699  *
700  * Validate and determine the length of an array of properties.
701  *
702  * PARAMS
703  *  iCount  [I] Length of the lpProps array
704  *  lpProps [I] Array of properties to validate/size
705  *  pcBytes [O] If non-NULL, destination for the size of the property array
706  *
707  * RETURNS
708  *  Success: S_OK. If pcBytes is non-NULL, it contains the size of the propery array.
709  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid or validation
710  *           of the property array fails.
711  */
712 SCODE WINAPI ScCountProps(INT iCount, LPSPropValue lpProps, ULONG *pcBytes)
713 {
714     ULONG i, ulCount = iCount, ulBytes = 0;
715
716     TRACE("(%d,%p,%p)\n", iCount, lpProps, pcBytes);
717
718     if (iCount <= 0 || !lpProps ||
719         IsBadReadPtr(lpProps, iCount * sizeof(SPropValue)))
720         return MAPI_E_INVALID_PARAMETER;
721
722     for (i = 0; i < ulCount; i++)
723     {
724         ULONG ulPropSize = 0;
725
726         if (FBadProp(&lpProps[i]) || lpProps[i].ulPropTag == PROP_ID_NULL ||
727             lpProps[i].ulPropTag == PROP_ID_INVALID)
728             return MAPI_E_INVALID_PARAMETER;
729
730             if (PROP_TYPE(lpProps[i].ulPropTag) != PT_OBJECT)
731             {
732                 ulPropSize = UlPropSize(&lpProps[i]);
733                 if (!ulPropSize)
734                     return MAPI_E_INVALID_PARAMETER;
735             }
736
737             switch (PROP_TYPE(lpProps[i].ulPropTag))
738             {
739             case PT_STRING8:
740             case PT_UNICODE:
741             case PT_CLSID:
742             case PT_BINARY:
743             case PT_MV_I2:
744             case PT_MV_I4:
745             case PT_MV_I8:
746             case PT_MV_R4:
747             case PT_MV_R8:
748             case PT_MV_CURRENCY:
749             case PT_MV_SYSTIME:
750             case PT_MV_APPTIME:
751                 ulPropSize += sizeof(SPropValue);
752                 break;
753             case PT_MV_CLSID:
754                 ulPropSize += lpProps[i].Value.MVszA.cValues * sizeof(char*) + sizeof(SPropValue);
755                 break;
756             case PT_MV_STRING8:
757             case PT_MV_UNICODE:
758                 ulPropSize += lpProps[i].Value.MVszA.cValues * sizeof(char*) + sizeof(SPropValue);
759                 break;
760             case PT_MV_BINARY:
761                 ulPropSize += lpProps[i].Value.MVbin.cValues * sizeof(SBinary) + sizeof(SPropValue);
762                 break;
763             default:
764                 ulPropSize = sizeof(SPropValue);
765                 break;
766             }
767             ulBytes += ulPropSize;
768     }
769     if (pcBytes)
770         *pcBytes = ulBytes;
771
772     return S_OK;
773 }
774
775 /*************************************************************************
776  * ScCopyProps@16 (MAPI32.171)
777  *
778  * Copy an array of property values into a buffer suited for serialisation.
779  *
780  * PARAMS
781  *  cValues   [I] Number of properties in lpProps
782  *  lpProps   [I] Property array to copy
783  *  lpDst     [O] Destination for the serialised data
784  *  lpCount   [O] If non-NULL, destination for the number of bytes of data written to lpDst
785  *
786  * RETURNS
787  *  Success: S_OK. lpDst contains the serialised data from lpProps.
788  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
789  *
790  * NOTES
791  *  The resulting property value array is stored in a contiguous block starting at lpDst.
792  */
793 SCODE WINAPI ScCopyProps(int cValues, LPSPropValue lpProps, LPVOID lpDst, ULONG *lpCount)
794 {
795     LPSPropValue lpDest = (LPSPropValue)lpDst;
796     char *lpDataDest = (char *)(lpDest + cValues);
797     ULONG ulLen, i;
798     int iter;
799
800     TRACE("(%d,%p,%p,%p)\n", cValues, lpProps, lpDst, lpCount);
801
802     if (!lpProps || cValues < 0 || !lpDest)
803         return MAPI_E_INVALID_PARAMETER;
804
805     memcpy(lpDst, lpProps, cValues * sizeof(SPropValue));
806
807     for (iter = 0; iter < cValues; iter++)
808     {
809         switch (PROP_TYPE(lpProps->ulPropTag))
810         {
811         case PT_CLSID:
812             lpDest->Value.lpguid = (LPGUID)lpDataDest;
813             *lpDest->Value.lpguid = *lpProps->Value.lpguid;
814             lpDataDest += sizeof(GUID);
815             break;
816         case PT_STRING8:
817             ulLen = lstrlenA(lpProps->Value.lpszA) + 1u;
818             lpDest->Value.lpszA = lpDataDest;
819             memcpy(lpDest->Value.lpszA, lpProps->Value.lpszA, ulLen);
820             lpDataDest += ulLen;
821             break;
822         case PT_UNICODE:
823             ulLen = (strlenW(lpProps->Value.lpszW) + 1u) * sizeof(WCHAR);
824             lpDest->Value.lpszW = (LPWSTR)lpDataDest;
825             memcpy(lpDest->Value.lpszW, lpProps->Value.lpszW, ulLen);
826             lpDataDest += ulLen;
827             break;
828         case PT_BINARY:
829             lpDest->Value.bin.lpb = (LPBYTE)lpDataDest;
830             memcpy(lpDest->Value.bin.lpb, lpProps->Value.bin.lpb, lpProps->Value.bin.cb);
831             lpDataDest += lpProps->Value.bin.cb;
832             break;
833         default:
834             if (lpProps->ulPropTag & MV_FLAG)
835             {
836                 lpDest->Value.MVi.cValues = lpProps->Value.MVi.cValues;
837                 /* Note: Assignment uses lppszA but covers all cases by union aliasing */
838                 lpDest->Value.MVszA.lppszA = (char**)lpDataDest;
839
840                 switch (PROP_TYPE(lpProps->ulPropTag))
841                 {
842                 case PT_MV_STRING8:
843                 {
844                     lpDataDest += lpProps->Value.MVszA.cValues * sizeof(char *);
845
846                     for (i = 0; i < lpProps->Value.MVszA.cValues; i++)
847                     {
848                         ULONG ulStrLen = lstrlenA(lpProps->Value.MVszA.lppszA[i]) + 1u;
849
850                         lpDest->Value.MVszA.lppszA[i] = lpDataDest;
851                         memcpy(lpDataDest, lpProps->Value.MVszA.lppszA[i], ulStrLen);
852                         lpDataDest += ulStrLen;
853                     }
854                     break;
855                 }
856                 case PT_MV_UNICODE:
857                 {
858                     lpDataDest += lpProps->Value.MVszW.cValues * sizeof(WCHAR *);
859
860                     for (i = 0; i < lpProps->Value.MVszW.cValues; i++)
861                     {
862                         ULONG ulStrLen = (strlenW(lpProps->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR);
863
864                         lpDest->Value.MVszW.lppszW[i] = (LPWSTR)lpDataDest;
865                         memcpy(lpDataDest, lpProps->Value.MVszW.lppszW[i], ulStrLen);
866                         lpDataDest += ulStrLen;
867                     }
868                     break;
869                 }
870                 case PT_MV_BINARY:
871                 {
872                     lpDataDest += lpProps->Value.MVszW.cValues * sizeof(SBinary);
873
874                     for (i = 0; i < lpProps->Value.MVszW.cValues; i++)
875                     {
876                         lpDest->Value.MVbin.lpbin[i].cb = lpProps->Value.MVbin.lpbin[i].cb;
877                         lpDest->Value.MVbin.lpbin[i].lpb = (LPBYTE)lpDataDest;
878                         memcpy(lpDataDest, lpProps->Value.MVbin.lpbin[i].lpb, lpDest->Value.MVbin.lpbin[i].cb);
879                         lpDataDest += lpDest->Value.MVbin.lpbin[i].cb;
880                     }
881                     break;
882                 }
883                 default:
884                     /* No embedded pointers, just copy the data over */
885                     ulLen = UlPropSize(lpProps);
886                     memcpy(lpDest->Value.MVi.lpi, lpProps->Value.MVi.lpi, ulLen);
887                     lpDataDest += ulLen;
888                     break;
889                 }
890                 break;
891             }
892         }
893         lpDest++;
894         lpProps++;
895     }
896     if (lpCount)
897         *lpCount = lpDataDest - (char *)lpDst;
898
899     return S_OK;
900 }
901
902 /*************************************************************************
903  * ScRelocProps@20 (MAPI32.172)
904  *
905  * Relocate the pointers in an array of property values after it has been copied.
906  *
907  * PARAMS
908  *  cValues   [I] Number of properties in lpProps
909  *  lpProps   [O] Property array to relocate the pointers in.
910  *  lpOld     [I] Position where the data was copied from
911  *  lpNew     [I] Position where the data was copied to
912  *  lpCount   [O] If non-NULL, destination for the number of bytes of data at lpDst
913  *
914  * RETURNS
915  *  Success: S_OK. Any pointers in lpProps are relocated.
916  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
917  *
918  * NOTES
919  *  MSDN states that this function can be used for serialisation by passing
920  *  NULL as either lpOld or lpNew, thus converting any pointers in lpProps
921  *  between offsets and pointers. This does not work in native (it crashes),
922  *  and cannot be made to work in Wine because the original interface design
923  *  is deficient. The only use left for this function is to remap pointers
924  *  in a contiguous property array that has been copied with memcpy() to
925  *  another memory location.
926  */
927 SCODE WINAPI ScRelocProps(int cValues, LPSPropValue lpProps, LPVOID lpOld,
928                           LPVOID lpNew, ULONG *lpCount)
929 {
930     static const BOOL bBadPtr = TRUE; /* Windows bug - Assumes source is bad */
931     LPSPropValue lpDest = lpProps;
932     ULONG ulCount = cValues * sizeof(SPropValue);
933     ULONG ulLen, i;
934     int iter;
935
936     TRACE("(%d,%p,%p,%p,%p)\n", cValues, lpProps, lpOld, lpNew, lpCount);
937
938     if (!lpProps || cValues < 0 || !lpOld || !lpNew)
939         return MAPI_E_INVALID_PARAMETER;
940
941     /* The reason native doesn't work as MSDN states is that it assumes that
942      * the lpProps pointer contains valid pointers. This is obviously not
943      * true if the array is being read back from serialisation (the pointers
944      * are just offsets). Native can't actually work converting the pointers to
945      * offsets either, because it converts any array pointers to offsets then
946      * _dereferences the offset_ in order to convert the array elements!
947      *
948      * The code below would handle both cases except that the design of this
949      * function makes it impossible to know when the pointers in lpProps are
950      * valid. If both lpOld and lpNew are non-NULL, native reads the pointers
951      * after converting them, so we must do the same. It seems this
952      * functionality was never tested by MS.
953      */
954
955 #define RELOC_PTR(p) (((char*)(p)) - (char*)lpOld + (char*)lpNew)
956
957     for (iter = 0; iter < cValues; iter++)
958     {
959         switch (PROP_TYPE(lpDest->ulPropTag))
960         {
961         case PT_CLSID:
962             lpDest->Value.lpguid = (LPGUID)RELOC_PTR(lpDest->Value.lpguid);
963             ulCount += sizeof(GUID);
964             break;
965         case PT_STRING8:
966             ulLen = bBadPtr ? 0 : lstrlenA(lpDest->Value.lpszA) + 1u;
967             lpDest->Value.lpszA = RELOC_PTR(lpDest->Value.lpszA);
968             if (bBadPtr)
969                 ulLen = lstrlenA(lpDest->Value.lpszA) + 1u;
970             ulCount += ulLen;
971             break;
972         case PT_UNICODE:
973             ulLen = bBadPtr ? 0 : (lstrlenW(lpDest->Value.lpszW) + 1u) * sizeof(WCHAR);
974             lpDest->Value.lpszW = (LPWSTR)RELOC_PTR(lpDest->Value.lpszW);
975             if (bBadPtr)
976                 ulLen = (strlenW(lpDest->Value.lpszW) + 1u) * sizeof(WCHAR);
977             ulCount += ulLen;
978             break;
979         case PT_BINARY:
980             lpDest->Value.bin.lpb = (LPBYTE)RELOC_PTR(lpDest->Value.bin.lpb);
981             ulCount += lpDest->Value.bin.cb;
982             break;
983         default:
984             if (lpDest->ulPropTag & MV_FLAG)
985             {
986                 /* Since we have to access the array elements, don't map the
987                  * array unless it is invalid (otherwise, map it at the end)
988                  */
989                 if (bBadPtr)
990                     lpDest->Value.MVszA.lppszA = (LPSTR*)RELOC_PTR(lpDest->Value.MVszA.lppszA);
991
992                 switch (PROP_TYPE(lpProps->ulPropTag))
993                 {
994                 case PT_MV_STRING8:
995                 {
996                     ulCount += lpDest->Value.MVszA.cValues * sizeof(char *);
997
998                     for (i = 0; i < lpDest->Value.MVszA.cValues; i++)
999                     {
1000                         ULONG ulStrLen = bBadPtr ? 0 : lstrlenA(lpDest->Value.MVszA.lppszA[i]) + 1u;
1001
1002                         lpDest->Value.MVszA.lppszA[i] = RELOC_PTR(lpDest->Value.MVszA.lppszA[i]);
1003                         if (bBadPtr)
1004                             ulStrLen = lstrlenA(lpDest->Value.MVszA.lppszA[i]) + 1u;
1005                         ulCount += ulStrLen;
1006                     }
1007                     break;
1008                 }
1009                 case PT_MV_UNICODE:
1010                 {
1011                     ulCount += lpDest->Value.MVszW.cValues * sizeof(WCHAR *);
1012
1013                     for (i = 0; i < lpDest->Value.MVszW.cValues; i++)
1014                     {
1015                         ULONG ulStrLen = bBadPtr ? 0 : (strlenW(lpDest->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR);
1016
1017                         lpDest->Value.MVszW.lppszW[i] = (LPWSTR)RELOC_PTR(lpDest->Value.MVszW.lppszW[i]);
1018                         if (bBadPtr)
1019                             ulStrLen = (strlenW(lpDest->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR);
1020                         ulCount += ulStrLen;
1021                     }
1022                     break;
1023                 }
1024                 case PT_MV_BINARY:
1025                 {
1026                     ulCount += lpDest->Value.MVszW.cValues * sizeof(SBinary);
1027
1028                     for (i = 0; i < lpDest->Value.MVszW.cValues; i++)
1029                     {
1030                         lpDest->Value.MVbin.lpbin[i].lpb = (LPBYTE)RELOC_PTR(lpDest->Value.MVbin.lpbin[i].lpb);
1031                         ulCount += lpDest->Value.MVbin.lpbin[i].cb;
1032                     }
1033                     break;
1034                 }
1035                 default:
1036                     ulCount += UlPropSize(lpDest);
1037                     break;
1038                 }
1039                 if (!bBadPtr)
1040                     lpDest->Value.MVszA.lppszA = (LPSTR*)RELOC_PTR(lpDest->Value.MVszA.lppszA);
1041                 break;
1042             }
1043         }
1044         lpDest++;
1045     }
1046     if (lpCount)
1047         *lpCount = ulCount;
1048
1049     return S_OK;
1050 }
1051
1052 /*************************************************************************
1053  * LpValFindProp@12 (MAPI32.173)
1054  *
1055  * Find a property with a given property id in a property array.
1056  *
1057  * PARAMS
1058  *  ulPropTag [I] Property tag containing property id to find
1059  *  cValues   [I] Number of properties in lpProps
1060  *  lpProps   [I] Property array to search
1061  *
1062  * RETURNS
1063  *  A pointer to the matching property, or NULL if none was found.
1064  *
1065  * NOTES
1066  *  This function matches only on the property id and does not care if the
1067  *  property types differ.
1068  */
1069 LPSPropValue WINAPI LpValFindProp(ULONG ulPropTag, ULONG cValues, LPSPropValue lpProps)
1070 {
1071     TRACE("(%d,%d,%p)\n", ulPropTag, cValues, lpProps);
1072
1073     if (lpProps && cValues)
1074     {
1075         ULONG i;
1076         for (i = 0; i < cValues; i++)
1077         {
1078             if (PROP_ID(ulPropTag) == PROP_ID(lpProps[i].ulPropTag))
1079                 return &lpProps[i];
1080         }
1081     }
1082     return NULL;
1083 }
1084
1085 /*************************************************************************
1086  * ScDupPropset@16 (MAPI32.174)
1087  *
1088  * Duplicate a property value array into a contiguous block of memory.
1089  *
1090  * PARAMS
1091  *  cValues   [I] Number of properties in lpProps
1092  *  lpProps   [I] Property array to duplicate
1093  *  lpAlloc   [I] Memory allocation function, use MAPIAllocateBuffer()
1094  *  lpNewProp [O] Destination for the newly duplicated property value array
1095  *
1096  * RETURNS
1097  *  Success: S_OK. *lpNewProp contains the duplicated array.
1098  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid,
1099  *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails.
1100  */
1101 SCODE WINAPI ScDupPropset(int cValues, LPSPropValue lpProps,
1102                           LPALLOCATEBUFFER lpAlloc, LPSPropValue *lpNewProp)
1103 {
1104     ULONG ulCount;
1105     SCODE sc;
1106
1107     TRACE("(%d,%p,%p,%p)\n", cValues, lpProps, lpAlloc, lpNewProp);
1108
1109     sc = ScCountProps(cValues, lpProps, &ulCount);
1110     if (SUCCEEDED(sc))
1111     {
1112         sc = lpAlloc(ulCount, (LPVOID*)lpNewProp);
1113         if (SUCCEEDED(sc))
1114             sc = ScCopyProps(cValues, lpProps, *lpNewProp, &ulCount);
1115     }
1116     return sc;
1117 }
1118
1119 /*************************************************************************
1120  * FBadRglpszA@8 (MAPI32.175)
1121  *
1122  * Determine if an array of strings is invalid
1123  *
1124  * PARAMS
1125  *  lppszStrs [I] Array of strings to check
1126  *  ulCount   [I] Number of strings in lppszStrs
1127  *
1128  * RETURNS
1129  *  TRUE, if lppszStrs is invalid, FALSE otherwise.
1130  */
1131 BOOL WINAPI FBadRglpszA(LPSTR *lppszStrs, ULONG ulCount)
1132 {
1133     ULONG i;
1134
1135     TRACE("(%p,%d)\n", lppszStrs, ulCount);
1136
1137     if (!ulCount)
1138         return FALSE;
1139
1140     if (!lppszStrs || IsBadReadPtr(lppszStrs, ulCount * sizeof(LPWSTR)))
1141         return TRUE;
1142
1143     for (i = 0; i < ulCount; i++)
1144     {
1145         if (!lppszStrs[i] || IsBadStringPtrA(lppszStrs[i], -1))
1146             return TRUE;
1147     }
1148     return FALSE;
1149 }
1150
1151 /*************************************************************************
1152  * FBadRglpszW@8 (MAPI32.176)
1153  *
1154  * See FBadRglpszA.
1155  */
1156 BOOL WINAPI FBadRglpszW(LPWSTR *lppszStrs, ULONG ulCount)
1157 {
1158     ULONG i;
1159
1160     TRACE("(%p,%d)\n", lppszStrs, ulCount);
1161
1162     if (!ulCount)
1163         return FALSE;
1164
1165     if (!lppszStrs || IsBadReadPtr(lppszStrs, ulCount * sizeof(LPWSTR)))
1166         return TRUE;
1167
1168     for (i = 0; i < ulCount; i++)
1169     {
1170         if (!lppszStrs[i] || IsBadStringPtrW(lppszStrs[i], -1))
1171             return TRUE;
1172     }
1173     return FALSE;
1174 }
1175
1176 /*************************************************************************
1177  * FBadRowSet@4 (MAPI32.177)
1178  *
1179  * Determine if a row is invalid
1180  *
1181  * PARAMS
1182  *  lpRow [I] Row to check
1183  *
1184  * RETURNS
1185  *  TRUE, if lpRow is invalid, FALSE otherwise.
1186  */
1187 BOOL WINAPI FBadRowSet(LPSRowSet lpRowSet)
1188 {
1189     ULONG i;
1190     TRACE("(%p)\n", lpRowSet);
1191
1192     if (!lpRowSet || IsBadReadPtr(lpRowSet, CbSRowSet(lpRowSet)))
1193         return TRUE;
1194
1195     for (i = 0; i < lpRowSet->cRows; i++)
1196     {
1197         if (FBadRow(&lpRowSet->aRow[i]))
1198             return TRUE;
1199     }
1200     return FALSE;
1201 }
1202
1203 /*************************************************************************
1204  * FBadPropTag@4 (MAPI32.179)
1205  *
1206  * Determine if a property tag is invalid
1207  *
1208  * PARAMS
1209  *  ulPropTag [I] Property tag to check
1210  *
1211  * RETURNS
1212  *  TRUE, if ulPropTag is invalid, FALSE otherwise.
1213  */
1214 ULONG WINAPI FBadPropTag(ULONG ulPropTag)
1215 {
1216     TRACE("(0x%08x)\n", ulPropTag);
1217
1218     switch (ulPropTag & (~MV_FLAG & PROP_TYPE_MASK))
1219     {
1220     case PT_UNSPECIFIED:
1221     case PT_NULL:
1222     case PT_I2:
1223     case PT_LONG:
1224     case PT_R4:
1225     case PT_DOUBLE:
1226     case PT_CURRENCY:
1227     case PT_APPTIME:
1228     case PT_ERROR:
1229     case PT_BOOLEAN:
1230     case PT_OBJECT:
1231     case PT_I8:
1232     case PT_STRING8:
1233     case PT_UNICODE:
1234     case PT_SYSTIME:
1235     case PT_CLSID:
1236     case PT_BINARY:
1237         return FALSE;
1238     }
1239     return TRUE;
1240 }
1241
1242 /*************************************************************************
1243  * FBadRow@4 (MAPI32.180)
1244  *
1245  * Determine if a row is invalid
1246  *
1247  * PARAMS
1248  *  lpRow [I] Row to check
1249  *
1250  * RETURNS
1251  *  TRUE, if lpRow is invalid, FALSE otherwise.
1252  */
1253 ULONG WINAPI FBadRow(LPSRow lpRow)
1254 {
1255     ULONG i;
1256     TRACE("(%p)\n", lpRow);
1257
1258     if (!lpRow || IsBadReadPtr(lpRow, sizeof(SRow)) || !lpRow->lpProps ||
1259         IsBadReadPtr(lpRow->lpProps, lpRow->cValues * sizeof(SPropValue)))
1260         return TRUE;
1261
1262     for (i = 0; i < lpRow->cValues; i++)
1263     {
1264         if (FBadProp(&lpRow->lpProps[i]))
1265             return TRUE;
1266     }
1267     return FALSE;
1268 }
1269
1270 /*************************************************************************
1271  * FBadProp@4 (MAPI32.181)
1272  *
1273  * Determine if a property is invalid
1274  *
1275  * PARAMS
1276  *  lpProp [I] Property to check
1277  *
1278  * RETURNS
1279  *  TRUE, if lpProp is invalid, FALSE otherwise.
1280  */
1281 ULONG WINAPI FBadProp(LPSPropValue lpProp)
1282 {
1283     if (!lpProp || IsBadReadPtr(lpProp, sizeof(SPropValue)) ||
1284         FBadPropTag(lpProp->ulPropTag))
1285         return TRUE;
1286
1287     switch (PROP_TYPE(lpProp->ulPropTag))
1288     {
1289     /* Single value properties containing pointers */
1290     case PT_STRING8:
1291         if (!lpProp->Value.lpszA || IsBadStringPtrA(lpProp->Value.lpszA, -1))
1292             return TRUE;
1293         break;
1294     case PT_UNICODE:
1295         if (!lpProp->Value.lpszW || IsBadStringPtrW(lpProp->Value.lpszW, -1))
1296             return TRUE;
1297         break;
1298     case PT_BINARY:
1299         if (IsBadReadPtr(lpProp->Value.bin.lpb, lpProp->Value.bin.cb))
1300             return TRUE;
1301         break;
1302     case PT_CLSID:
1303         if (IsBadReadPtr(lpProp->Value.lpguid, sizeof(GUID)))
1304             return TRUE;
1305         break;
1306
1307     /* Multiple value properties (arrays) containing no pointers */
1308     case PT_MV_I2:
1309         return PROP_BadArray(lpProp, sizeof(SHORT));
1310     case PT_MV_LONG:
1311         return PROP_BadArray(lpProp, sizeof(LONG));
1312     case PT_MV_LONGLONG:
1313         return PROP_BadArray(lpProp, sizeof(LONG64));
1314     case PT_MV_FLOAT:
1315         return PROP_BadArray(lpProp, sizeof(float));
1316     case PT_MV_SYSTIME:
1317         return PROP_BadArray(lpProp, sizeof(FILETIME));
1318     case PT_MV_APPTIME:
1319     case PT_MV_DOUBLE:
1320         return PROP_BadArray(lpProp, sizeof(double));
1321     case PT_MV_CURRENCY:
1322         return PROP_BadArray(lpProp, sizeof(CY));
1323     case PT_MV_CLSID:
1324         return PROP_BadArray(lpProp, sizeof(GUID));
1325
1326     /* Multiple value properties containing pointers */
1327     case PT_MV_STRING8:
1328         return FBadRglpszA(lpProp->Value.MVszA.lppszA,
1329                            lpProp->Value.MVszA.cValues);
1330     case PT_MV_UNICODE:
1331         return FBadRglpszW(lpProp->Value.MVszW.lppszW,
1332                            lpProp->Value.MVszW.cValues);
1333     case PT_MV_BINARY:
1334         return FBadEntryList(&lpProp->Value.MVbin);
1335     }
1336     return FALSE;
1337 }
1338
1339 /*************************************************************************
1340  * FBadColumnSet@4 (MAPI32.182)
1341  *
1342  * Determine if an array of property tags is invalid
1343  *
1344  * PARAMS
1345  *  lpCols [I] Property tag array to check
1346  *
1347  * RETURNS
1348  *  TRUE, if lpCols is invalid, FALSE otherwise.
1349  */
1350 ULONG WINAPI FBadColumnSet(LPSPropTagArray lpCols)
1351 {
1352     ULONG ulRet = FALSE, i;
1353
1354     TRACE("(%p)\n", lpCols);
1355
1356     if (!lpCols || IsBadReadPtr(lpCols, CbSPropTagArray(lpCols)))
1357         ulRet = TRUE;
1358     else
1359     {
1360         for (i = 0; i < lpCols->cValues; i++)
1361         {
1362             if ((lpCols->aulPropTag[i] & PROP_TYPE_MASK) == PT_ERROR ||
1363                 FBadPropTag(lpCols->aulPropTag[i]))
1364             {
1365                 ulRet = TRUE;
1366                 break;
1367             }
1368         }
1369     }
1370     TRACE("Returning %s\n", ulRet ? "TRUE" : "FALSE");
1371     return ulRet;
1372 }
1373
1374
1375 /**************************************************************************
1376  *  IMAPIProp {MAPI32}
1377  *
1378  * The default Mapi interface for manipulating object properties.
1379  *
1380  * DESCRIPTION
1381  *  This object provides an interface to an objects properties. It is exposed
1382  *  by several types of Mapi objects in order to simplify the querying and
1383  *  modification of properties.
1384  *
1385  * METHODS
1386  */
1387
1388 /* A single property in a property data collection */
1389 typedef struct
1390 {
1391   struct list  entry;
1392   ULONG        ulAccess; /* The property value access level */
1393   LPSPropValue value;    /* The property value */
1394 } IPropDataItem, *LPIPropDataItem;
1395
1396  /* The main property data collection structure */
1397 typedef struct
1398 {
1399     const IPropDataVtbl   *lpVtbl;
1400     LONG             lRef;        /* Reference count */
1401     ALLOCATEBUFFER  *lpAlloc;     /* Memory allocation routine */
1402     ALLOCATEMORE    *lpMore;      /* Linked memory allocation routine */
1403     FREEBUFFER      *lpFree;      /* Memory free routine */
1404     ULONG            ulObjAccess; /* Object access level */
1405     ULONG            ulNumValues; /* Number of items in values list */
1406     struct list      values;      /* List of property values */
1407     CRITICAL_SECTION cs;          /* Lock for thread safety */
1408 } IPropDataImpl;
1409
1410 /* Internal - Get a property value, assumes lock is held */
1411 static IPropDataItem *IMAPIPROP_GetValue(IPropDataImpl *This, ULONG ulPropTag)
1412 {
1413     struct list *cursor;
1414
1415     LIST_FOR_EACH(cursor, &This->values)
1416     {
1417         LPIPropDataItem current = LIST_ENTRY(cursor, IPropDataItem, entry);
1418         /* Note that propery types don't have to match, just Id's */
1419         if (PROP_ID(current->value->ulPropTag) == PROP_ID(ulPropTag))
1420             return current;
1421     }
1422     return NULL;
1423 }
1424
1425 /* Internal - Add a new property value, assumes lock is held */
1426 static IPropDataItem *IMAPIPROP_AddValue(IPropDataImpl *This,
1427                                          LPSPropValue lpProp)
1428 {
1429     LPVOID lpMem;
1430     LPIPropDataItem lpNew;
1431     HRESULT hRet;
1432
1433     hRet = This->lpAlloc(sizeof(IPropDataItem), &lpMem);
1434
1435     if (SUCCEEDED(hRet))
1436     {
1437         lpNew = lpMem;
1438         lpNew->ulAccess = IPROP_READWRITE;
1439
1440         /* Allocate the value separately so we can update it easily */
1441         lpMem = NULL;
1442         hRet = This->lpAlloc(sizeof(SPropValue), &lpMem);
1443         if (SUCCEEDED(hRet))
1444         {
1445             lpNew->value = lpMem;
1446
1447             hRet = PropCopyMore(lpNew->value, lpProp, This->lpMore, lpMem);
1448             if (SUCCEEDED(hRet))
1449             {
1450                 list_add_tail(&This->values, &lpNew->entry);
1451                 This->ulNumValues++;
1452                 return lpNew;
1453             }
1454             This->lpFree(lpNew->value);
1455         }
1456         This->lpFree(lpNew);
1457     }
1458     return NULL;
1459 }
1460
1461 /* Internal - Lock an IPropData object */
1462 static inline void IMAPIPROP_Lock(IPropDataImpl *This)
1463 {
1464     EnterCriticalSection(&This->cs);
1465 }
1466
1467 /* Internal - Unlock an IPropData object */
1468 static inline void IMAPIPROP_Unlock(IPropDataImpl *This)
1469 {
1470     LeaveCriticalSection(&This->cs);
1471 }
1472
1473 /* This one seems to be missing from mapidefs.h */
1474 #define CbNewSPropProblemArray(c) \
1475     (offsetof(SPropProblemArray,aProblem)+(c)*sizeof(SPropProblem))
1476
1477 /**************************************************************************
1478  *  IMAPIProp_QueryInterface {MAPI32}
1479  *
1480  * Inherited method from the IUnknown Interface.
1481  * See IUnknown_QueryInterface.
1482  *
1483  * NOTES
1484  * This object exposes the following interfaces:
1485  * - IUnknown() : The default interface for all COM-Objects.
1486  * - IMAPIProp() : The default Mapi interface for manipulating object properties.
1487  */
1488 static inline HRESULT IMAPIProp_fnQueryInterface(LPMAPIPROP iface, REFIID riid, LPVOID *ppvObj)
1489 {
1490     IPropDataImpl *This = (IPropDataImpl*)iface;
1491
1492     TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppvObj);
1493
1494     if (!ppvObj || !riid)
1495         return MAPI_E_INVALID_PARAMETER;
1496
1497     *ppvObj = NULL;
1498
1499     if(IsEqualIID(riid, &IID_IUnknown) ||
1500        IsEqualIID(riid, &IID_IMAPIProp) ||
1501        IsEqualIID(riid, &IID_IMAPIPropData))
1502     {
1503         *ppvObj = This;
1504         IPropData_AddRef(iface);
1505         TRACE("returning %p\n", *ppvObj);
1506         return S_OK;
1507     }
1508
1509     TRACE("returning E_NOINTERFACE\n");
1510     return MAPI_E_INTERFACE_NOT_SUPPORTED;
1511 }
1512
1513 /**************************************************************************
1514  *  IMAPIProp_AddRef {MAPI32}
1515  *
1516  * Inherited method from the IUnknown Interface.
1517  * See IUnknown_AddRef.
1518  */
1519 static inline ULONG IMAPIProp_fnAddRef(LPMAPIPROP iface)
1520 {
1521     IPropDataImpl *This = (IPropDataImpl*)iface;
1522
1523     TRACE("(%p)->(count before=%u)\n", This, This->lRef);
1524
1525     return InterlockedIncrement(&This->lRef);
1526 }
1527
1528 /**************************************************************************
1529  *  IMAPIProp_Release {MAPI32}
1530  *
1531  * Inherited method from the IUnknown Interface.
1532  * See IUnknown_Release.
1533  */
1534 static inline ULONG IMAPIProp_fnRelease(LPMAPIPROP iface)
1535 {
1536     IPropDataImpl *This = (IPropDataImpl*)iface;
1537     LONG lRef;
1538
1539     TRACE("(%p)->(count before=%u)\n", This, This->lRef);
1540
1541     lRef = InterlockedDecrement(&This->lRef);
1542     if (!lRef)
1543     {
1544         TRACE("Destroying IPropData (%p)\n",This);
1545
1546         /* Note: No need to lock, since no other thread is referencing iface */
1547         while (!list_empty(&This->values))
1548         {
1549             struct list *head = list_head(&This->values);
1550             LPIPropDataItem current = LIST_ENTRY(head, IPropDataItem, entry);
1551             list_remove(head);
1552             This->lpFree(current->value);
1553             This->lpFree(current);
1554         }
1555         This->cs.DebugInfo->Spare[0] = 0;
1556         DeleteCriticalSection(&This->cs);
1557         This->lpFree(This);
1558     }
1559     return (ULONG)lRef;
1560 }
1561
1562 /**************************************************************************
1563  *  IMAPIProp_GetLastError {MAPI32}
1564  *
1565  * Get information about the last error that occurred in an IMAPIProp object.
1566  *
1567  * PARAMS
1568  *  iface    [I] IMAPIProp object that experienced the error
1569  *  hRes     [I] Result of the call that returned an error
1570  *  ulFlags  [I] 0=return Ascii strings, MAPI_UNICODE=return Unicode strings
1571  *  lppError [O] Destination for detailed error information
1572  *
1573  * RETURNS
1574  *  Success: S_OK. *lppError contains details about the last error.
1575  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid,
1576  *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails.
1577  *
1578  * NOTES
1579  *  - If this function succeeds, the returned information in *lppError must be
1580  *  freed using MAPIFreeBuffer() once the caller is finished with it.
1581  *  - It is possible for this function to succeed and set *lppError to NULL,
1582  *  if there is no further information to report about hRes.
1583  */
1584 static inline HRESULT
1585 IMAPIProp_fnGetLastError(LPMAPIPROP iface, HRESULT hRes,
1586                          ULONG ulFlags, LPMAPIERROR *lppError)
1587 {
1588     TRACE("(%p,0x%08X,0x%08X,%p)\n", iface, hRes, ulFlags, lppError);
1589
1590     if (!lppError  || SUCCEEDED(hRes) || (ulFlags & ~MAPI_UNICODE))
1591         return MAPI_E_INVALID_PARAMETER;
1592
1593     *lppError = NULL;
1594     return S_OK;
1595 }
1596
1597 /**************************************************************************
1598  *  IMAPIProp_SaveChanges {MAPI32}
1599  *
1600  * Update any changes made to a transactional IMAPIProp object.
1601  *
1602  * PARAMS
1603  *  iface    [I] IMAPIProp object to update
1604  *  ulFlags  [I] Flags controlling the update.
1605  *
1606  * RETURNS
1607  *  Success: S_OK. Any outstanding changes are committed to the object.
1608  *  Failure: An HRESULT error code describing the error.
1609  */
1610 static inline HRESULT
1611 IMAPIProp_fnSaveChanges(LPMAPIPROP iface, ULONG ulFlags)
1612 {
1613     TRACE("(%p,0x%08X)\n", iface, ulFlags);
1614
1615      /* Since this object is not transacted we do not need to implement this */
1616      /* FIXME: Should we set the access levels to clean? */
1617     return S_OK;
1618 }
1619
1620 /**************************************************************************
1621  *  IMAPIProp_GetProps {MAPI32}
1622  *
1623  * Get property values from an IMAPIProp object.
1624  *
1625  * PARAMS
1626  *  iface    [I] IMAPIProp object to get the property values from
1627  *  lpTags   [I] Property tage of property values to be retrieved
1628  *  ulFlags  [I] Return 0=Ascii MAPI_UNICODE=Unicode strings for
1629  *                 unspecified types
1630  *  lpCount  [O] Destination for number of properties returned
1631  *  lppProps [O] Destination for returned property values
1632  *
1633  * RETURNS
1634  *  Success: S_OK. *lppProps and *lpCount are updated.
1635  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
1636  *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails, or
1637  *           MAPI_W_ERRORS_RETURNED if not all properties were retrieved
1638  *           successfully.
1639  * NOTES
1640  *  - If MAPI_W_ERRORS_RETURNED is returned, any properties that could not be
1641  *    retrieved from iface are present in lppProps with their type
1642  *    changed to PT_ERROR and Id unchanged.
1643  */
1644 static inline HRESULT
1645 IMAPIProp_fnGetProps(LPMAPIPROP iface, LPSPropTagArray lpTags,
1646                      ULONG ulFlags, ULONG *lpCount, LPSPropValue *lppProps)
1647 {
1648     ULONG i;
1649     HRESULT hRet = S_OK;
1650     IPropDataImpl *This = (IPropDataImpl*)iface;
1651
1652     TRACE("(%p,%p,0x%08x,%p,%p) stub\n", iface, lpTags, ulFlags,
1653           lpCount, lppProps);
1654
1655     if (!iface || ulFlags & ~MAPI_UNICODE || !lpTags || *lpCount || !lppProps)
1656         return MAPI_E_INVALID_PARAMETER;
1657
1658     FIXME("semi-stub, flags not supported\n");
1659
1660     *lpCount = lpTags->cValues;
1661     *lppProps = NULL;
1662
1663     if (*lpCount)
1664     {
1665         hRet = MAPIAllocateBuffer(*lpCount * sizeof(SPropValue), (LPVOID*)lppProps);
1666         if (FAILED(hRet))
1667             return hRet;
1668
1669         IMAPIPROP_Lock(This);
1670
1671         for (i = 0; i < lpTags->cValues; i++)
1672         {
1673             HRESULT hRetTmp = E_INVALIDARG;
1674             LPIPropDataItem item;
1675
1676             item = IMAPIPROP_GetValue(This, lpTags->aulPropTag[i]);
1677
1678             if (item)
1679                 hRetTmp = PropCopyMore(&(*lppProps)[i], item->value,
1680                                        This->lpMore, *lppProps);
1681             if (FAILED(hRetTmp))
1682             {
1683                 hRet = MAPI_W_ERRORS_RETURNED;
1684                 (*lppProps)[i].ulPropTag =
1685                     CHANGE_PROP_TYPE(lpTags->aulPropTag[i], PT_ERROR);
1686             }
1687         }
1688
1689         IMAPIPROP_Unlock(This);
1690     }
1691     return hRet;
1692 }
1693
1694 /**************************************************************************
1695  *  MAPIProp_GetPropList {MAPI32}
1696  *
1697  * Get the list of property tags for all values in an IMAPIProp object.
1698  *
1699  * PARAMS
1700  *  iface   [I] IMAPIProp object to get the property tag list from
1701  *  ulFlags [I] Return 0=Ascii MAPI_UNICODE=Unicode strings for
1702  *              unspecified types
1703  *  lppTags [O] Destination for the retrieved property tag list
1704  *
1705  * RETURNS
1706  *  Success: S_OK. *lppTags contains the tags for all available properties.
1707  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
1708  *           MAPI_E_BAD_CHARWIDTH, if Ascii or Unicode strings are requested
1709  *           and that type of string is not supported.
1710  */
1711 static inline HRESULT
1712 IMAPIProp_fnGetPropList(LPMAPIPROP iface, ULONG ulFlags,
1713                         LPSPropTagArray *lppTags)
1714 {
1715     IPropDataImpl *This = (IPropDataImpl*)iface;
1716     ULONG i;
1717     HRESULT hRet;
1718
1719     TRACE("(%p,0x%08x,%p) stub\n", iface, ulFlags, lppTags);
1720
1721     if (!iface || ulFlags & ~MAPI_UNICODE || !lppTags)
1722         return MAPI_E_INVALID_PARAMETER;
1723
1724     FIXME("semi-stub, flags not supported\n");
1725
1726     *lppTags = NULL;
1727
1728     IMAPIPROP_Lock(This);
1729
1730     hRet = MAPIAllocateBuffer(CbNewSPropTagArray(This->ulNumValues),
1731                               (LPVOID*)lppTags);
1732     if (SUCCEEDED(hRet))
1733     {
1734         struct list *cursor;
1735
1736         i = 0;
1737         LIST_FOR_EACH(cursor, &This->values)
1738         {
1739             LPIPropDataItem current = LIST_ENTRY(cursor, IPropDataItem, entry);
1740             (*lppTags)->aulPropTag[i] = current->value->ulPropTag;
1741             i++;
1742         }
1743         (*lppTags)->cValues = This->ulNumValues;
1744     }
1745
1746     IMAPIPROP_Unlock(This);
1747     return hRet;
1748 }
1749
1750 /**************************************************************************
1751  *  IMAPIProp_OpenProperty {MAPI32}
1752  *
1753  * Not documented at this time.
1754  *
1755  * RETURNS
1756  *  An HRESULT success/failure code.
1757  */
1758 static inline HRESULT
1759 IMAPIProp_fnOpenProperty(LPMAPIPROP iface, ULONG ulPropTag, LPCIID iid,
1760                          ULONG ulOpts, ULONG ulFlags, LPUNKNOWN *lpUnk)
1761 {
1762     FIXME("(%p,%u,%s,%u,0x%08x,%p) stub\n", iface, ulPropTag,
1763           debugstr_guid(iid), ulOpts, ulFlags, lpUnk);
1764     return MAPI_E_NO_SUPPORT;
1765 }
1766
1767
1768 /**************************************************************************
1769  *  IMAPIProp_SetProps {MAPI32}
1770  *
1771  * Add or edit the property values in an IMAPIProp object.
1772  *
1773  * PARAMS
1774  *  iface    [I] IMAPIProp object to get the property tag list from
1775  *  ulValues [I] Number of properties in lpProps
1776  *  lpProps  [I] Property values to set
1777  *  lppProbs [O] Optional destination for any problems that occurred
1778  *
1779  * RETURNS
1780  *  Success: S_OK. The properties in lpProps are added to iface if they don't
1781  *           exist, or changed to the values in lpProps if they do
1782  *  Failure: An HRESULT error code describing the error
1783  */
1784 static inline HRESULT
1785 IMAPIProp_fnSetProps(LPMAPIPROP iface, ULONG ulValues,
1786                      LPSPropValue lpProps, LPSPropProblemArray *lppProbs)
1787 {
1788     IPropDataImpl *This = (IPropDataImpl*)iface;
1789     HRESULT hRet = S_OK;
1790     ULONG i;
1791
1792     TRACE("(%p,%u,%p,%p)\n", iface, ulValues, lpProps, lppProbs);
1793
1794     if (!iface || !lpProps)
1795       return MAPI_E_INVALID_PARAMETER;
1796
1797     for (i = 0; i < ulValues; i++)
1798     {
1799         if (FBadProp(&lpProps[i]) ||
1800             PROP_TYPE(lpProps[i].ulPropTag) == PT_OBJECT ||
1801             PROP_TYPE(lpProps[i].ulPropTag) == PT_NULL)
1802           return MAPI_E_INVALID_PARAMETER;
1803     }
1804
1805     IMAPIPROP_Lock(This);
1806
1807     /* FIXME: Under what circumstances is lpProbs created? */
1808     for (i = 0; i < ulValues; i++)
1809     {
1810         LPIPropDataItem item = IMAPIPROP_GetValue(This, lpProps[i].ulPropTag);
1811
1812         if (item)
1813         {
1814             HRESULT hRetTmp;
1815             LPVOID lpMem = NULL;
1816
1817             /* Found, so update the existing value */
1818             if (item->value->ulPropTag != lpProps[i].ulPropTag)
1819                 FIXME("semi-stub, overwriting type (not coercing)\n");
1820
1821             hRetTmp = This->lpAlloc(sizeof(SPropValue), &lpMem);
1822             if (SUCCEEDED(hRetTmp))
1823             {
1824                 hRetTmp = PropCopyMore(lpMem, &lpProps[i], This->lpMore, lpMem);
1825                 if (SUCCEEDED(hRetTmp))
1826                 {
1827                     This->lpFree(item->value);
1828                     item->value = lpMem;
1829                     continue;
1830                 }
1831                 This->lpFree(lpMem);
1832             }
1833             hRet = hRetTmp;
1834         }
1835         else
1836         {
1837             /* Add new value */
1838             if (!IMAPIPROP_AddValue(This, &lpProps[i]))
1839                 hRet = MAPI_E_NOT_ENOUGH_MEMORY;
1840         }
1841     }
1842
1843     IMAPIPROP_Unlock(This);
1844     return hRet;
1845 }
1846
1847 /**************************************************************************
1848  *  IMAPIProp_DeleteProps {MAPI32}
1849  *
1850  * Delete one or more property values from an IMAPIProp object.
1851  *
1852  * PARAMS
1853  *  iface    [I] IMAPIProp object to remove property values from.
1854  *  lpTags   [I] Collection of property Id's to remove from iface.
1855  *  lppProbs [O] Destination for problems encountered, if any.
1856  *
1857  * RETURNS
1858  *  Success: S_OK. Any properties in iface matching property Id's in lpTags have
1859  *           been deleted. If lppProbs is non-NULL it contains details of any
1860  *           errors that occurred.
1861  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
1862  *           E_ACCESSDENIED, if this object was created using CreateIProp() and
1863  *           a subsequent call to IPropData_SetObjAcess() was made specifying
1864  *           IPROP_READONLY as the access type.
1865  *
1866  * NOTES
1867  *  - lppProbs will not be populated for cases where a property Id is present
1868  *    in lpTags but not in iface.
1869  *  - lppProbs should be deleted with MAPIFreeBuffer() if returned.
1870  */
1871 static inline HRESULT
1872 IMAPIProp_fnDeleteProps(LPMAPIPROP iface, LPSPropTagArray lpTags,
1873                         LPSPropProblemArray *lppProbs)
1874 {
1875     IPropDataImpl *This = (IPropDataImpl*)iface;
1876     ULONG i, numProbs = 0;
1877     HRESULT hRet = S_OK;
1878
1879     TRACE("(%p,%p,%p)\n", iface, lpTags, lppProbs);
1880
1881     if (!iface || !lpTags)
1882         return MAPI_E_INVALID_PARAMETER;
1883
1884     if (lppProbs)
1885         *lppProbs = NULL;
1886
1887     for (i = 0; i < lpTags->cValues; i++)
1888     {
1889         if (FBadPropTag(lpTags->aulPropTag[i]) ||
1890             PROP_TYPE(lpTags->aulPropTag[i]) == PT_OBJECT ||
1891             PROP_TYPE(lpTags->aulPropTag[i]) == PT_NULL)
1892           return MAPI_E_INVALID_PARAMETER;
1893     }
1894
1895     IMAPIPROP_Lock(This);
1896
1897     if (This->ulObjAccess != IPROP_READWRITE)
1898     {
1899         IMAPIPROP_Unlock(This);
1900         return E_ACCESSDENIED;
1901     }
1902
1903     for (i = 0; i < lpTags->cValues; i++)
1904     {
1905         LPIPropDataItem item = IMAPIPROP_GetValue(This, lpTags->aulPropTag[i]);
1906
1907         if (item)
1908         {
1909             if (item->ulAccess & IPROP_READWRITE)
1910             {
1911                 /* Everything hunky-dory, remove the item */
1912                 list_remove(&item->entry);
1913                 This->lpFree(item->value); /* Also frees value pointers */
1914                 This->lpFree(item);
1915                 This->ulNumValues--;
1916             }
1917             else if (lppProbs)
1918             {
1919                  /* Can't write the value. Create/populate problems array */
1920                  if (!*lppProbs)
1921                  {
1922                      /* Create problems array */
1923                      ULONG ulSize = CbNewSPropProblemArray(lpTags->cValues - i);
1924                      HRESULT hRetTmp = MAPIAllocateBuffer(ulSize, (LPVOID*)lppProbs);
1925                      if (FAILED(hRetTmp))
1926                          hRet = hRetTmp;
1927                  }
1928                  if (*lppProbs)
1929                  {
1930                      LPSPropProblem lpProb = &(*lppProbs)->aProblem[numProbs];
1931                      lpProb->ulIndex = i;
1932                      lpProb->ulPropTag = lpTags->aulPropTag[i];
1933                      lpProb->scode = E_ACCESSDENIED;
1934                      numProbs++;
1935                  }
1936             }
1937         }
1938     }
1939     if (lppProbs && *lppProbs)
1940         (*lppProbs)->cProblem = numProbs;
1941
1942     IMAPIPROP_Unlock(This);
1943     return hRet;
1944 }
1945
1946
1947 /**************************************************************************
1948  *  IMAPIProp_CopyTo {MAPI32}
1949  *
1950  * Not documented at this time.
1951  *
1952  * RETURNS
1953  *  An HRESULT success/failure code.
1954  */
1955 static inline HRESULT
1956 IMAPIProp_fnCopyTo(LPMAPIPROP iface, ULONG niids, LPCIID lpiidExcl,
1957                    LPSPropTagArray lpPropsExcl, ULONG ulParam,
1958                    LPMAPIPROGRESS lpIProgress, LPCIID lpIfaceIid, LPVOID lpDstObj,
1959                    ULONG ulFlags, LPSPropProblemArray *lppProbs)
1960 {
1961     FIXME("(%p,%u,%p,%p,%x,%p,%s,%p,0x%08X,%p) stub\n", iface, niids,
1962           lpiidExcl, lpPropsExcl, ulParam, lpIProgress,
1963           debugstr_guid(lpIfaceIid), lpDstObj, ulFlags, lppProbs);
1964     return MAPI_E_NO_SUPPORT;
1965 }
1966
1967 /**************************************************************************
1968  *  IMAPIProp_CopyProps {MAPI32}
1969  *
1970  * Not documented at this time.
1971  *
1972  * RETURNS
1973  *  An HRESULT success/failure code.
1974  */
1975 static inline HRESULT
1976 IMAPIProp_fnCopyProps(LPMAPIPROP iface, LPSPropTagArray lpInclProps,
1977                       ULONG ulParam, LPMAPIPROGRESS lpIProgress, LPCIID lpIface,
1978                       LPVOID lpDstObj, ULONG ulFlags,
1979                       LPSPropProblemArray *lppProbs)
1980 {
1981     FIXME("(%p,%p,%x,%p,%s,%p,0x%08X,%p) stub\n", iface, lpInclProps,
1982           ulParam, lpIProgress, debugstr_guid(lpIface), lpDstObj, ulFlags,
1983           lppProbs);
1984     return MAPI_E_NO_SUPPORT;
1985 }
1986
1987 /**************************************************************************
1988  *  IMAPIProp_GetNamesFromIDs {MAPI32}
1989  *
1990  * Get the names of properties from their identifiers.
1991  *
1992  * PARAMS
1993  *  iface       [I]   IMAPIProp object to operate on
1994  *  lppPropTags [I/O] Property identifiers to get the names for, or NULL to
1995  *                    get all names
1996  *  iid         [I]   Property set identifier, or NULL
1997  *  ulFlags     [I]   MAPI_NO_IDS=Don't return numeric named properties,
1998  *                    or MAPI_NO_STRINGS=Don't return strings
1999  *  lpCount     [O]   Destination for number of properties returned
2000  *  lpppNames   [O]   Destination for returned names
2001  *
2002  * RETURNS
2003  *  Success: S_OK. *lppPropTags and lpppNames contain the returned
2004  *           name/identifiers.
2005  *  Failure: MAPI_E_NO_SUPPORT, if the object does not support named properties,
2006  *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails, or
2007  *           MAPI_W_ERRORS_RETURNED if not all properties were retrieved
2008  *           successfully.
2009  */
2010 static inline HRESULT
2011 IMAPIProp_fnGetNamesFromIDs(LPMAPIPROP iface, LPSPropTagArray *lppPropTags,
2012                             LPGUID iid, ULONG ulFlags, ULONG *lpCount,
2013                             LPMAPINAMEID **lpppNames)
2014 {
2015     FIXME("(%p,%p,%s,0x%08X,%p,%p) stub\n", iface, lppPropTags,
2016           debugstr_guid(iid), ulFlags, lpCount, lpppNames);
2017     return MAPI_E_NO_SUPPORT;
2018 }
2019
2020 /**************************************************************************
2021  *  IMAPIProp_GetIDsFromNames {MAPI32}
2022  *
2023  * Get property identifiers associated with one or more named properties.
2024  *
2025  * PARAMS
2026  *  iface       [I] IMAPIProp object to operate on
2027  *  ulNames     [I] Number of names in lppNames
2028  *  lppNames    [I] Names to query or create, or NULL to query all names
2029  *  ulFlags     [I] Pass MAPI_CREATE to create new named properties
2030  *  lppPropTags [O] Destination for queried or created property identifiers
2031  *
2032  * RETURNS
2033  *  Success: S_OK. *lppPropTags contains the property tags created or requested.
2034  *  Failure: MAPI_E_NO_SUPPORT, if the object does not support named properties,
2035  *           MAPI_E_TOO_BIG, if the object cannot process the number of
2036  *           properties involved.
2037  *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails, or
2038  *           MAPI_W_ERRORS_RETURNED if not all properties were retrieved
2039  *           successfully.
2040  */
2041 static inline HRESULT
2042 IMAPIProp_fnGetIDsFromNames(LPMAPIPROP iface, ULONG ulNames,
2043                             LPMAPINAMEID *lppNames, ULONG ulFlags,
2044                             LPSPropTagArray *lppPropTags)
2045 {
2046     FIXME("(%p,%d,%p,0x%08X,%p) stub\n",
2047           iface, ulNames, lppNames, ulFlags, lppPropTags);
2048     return MAPI_E_NO_SUPPORT;
2049 }
2050
2051 /**************************************************************************
2052  *  IPropData {MAPI32}
2053  *
2054  * A default Mapi interface to provide manipulation of object properties.
2055  *
2056  * DESCRIPTION
2057  *  This object provides a default interface suitable in some cases as an
2058  *  implementation of the IMAPIProp interface (which has no default
2059  *  implementation). In addition to the IMAPIProp() methods inherited, this
2060  *  interface allows read/write control over access to the object and its
2061  *  individual properties.
2062  *
2063  *  To obtain the default implementation of this interface from Mapi, call
2064  *  CreateIProp().
2065  *
2066  * METHODS
2067  */
2068
2069 /**************************************************************************
2070  *  IPropData_QueryInterface {MAPI32}
2071  *
2072  * Inherited method from the IMAPIProp Interface.
2073  * See IMAPIProp_QueryInterface.
2074  */
2075 static HRESULT WINAPI
2076 IPropData_fnQueryInterface(LPPROPDATA iface, REFIID riid, LPVOID *ppvObj)
2077 {
2078     return IMAPIProp_fnQueryInterface((LPMAPIPROP)iface, riid, ppvObj);
2079 }
2080
2081 /**************************************************************************
2082  *  IPropData_AddRef {MAPI32}
2083  *
2084  * Inherited method from the IMAPIProp Interface.
2085  * See IMAPIProp_AddRef.
2086  */
2087 static ULONG WINAPI
2088 IPropData_fnAddRef(LPPROPDATA iface)
2089 {
2090     return IMAPIProp_fnAddRef((LPMAPIPROP)iface);
2091 }
2092
2093 /**************************************************************************
2094  *  IPropData_Release {MAPI32}
2095  *
2096  * Inherited method from the IMAPIProp Interface.
2097  * See IMAPIProp_Release.
2098  */
2099 static ULONG WINAPI
2100 IPropData_fnRelease(LPPROPDATA iface)
2101 {
2102     return IMAPIProp_fnRelease((LPMAPIPROP)iface);
2103 }
2104
2105 /**************************************************************************
2106  *  IPropData_GetLastError {MAPI32}
2107  *
2108  * Inherited method from the IMAPIProp Interface.
2109  * See IMAPIProp_GetLastError.
2110  */
2111 static HRESULT WINAPI
2112 IPropData_fnGetLastError(LPPROPDATA iface, HRESULT hRes, ULONG ulFlags,
2113                          LPMAPIERROR *lppError)
2114 {
2115     return IMAPIProp_fnGetLastError((LPMAPIPROP)iface, hRes, ulFlags, lppError);
2116 }
2117
2118 /**************************************************************************
2119  *  IPropData_SaveChanges {MAPI32}
2120  *
2121  * Inherited method from the IMAPIProp Interface.
2122  * See IMAPIProp_SaveChanges.
2123  */
2124 static HRESULT WINAPI
2125 IPropData_fnSaveChanges(LPPROPDATA iface, ULONG ulFlags)
2126 {
2127     return IMAPIProp_fnSaveChanges((LPMAPIPROP)iface, ulFlags);
2128 }
2129
2130 /**************************************************************************
2131  *  IPropData_GetProps {MAPI32}
2132  *
2133  * Inherited method from the IMAPIProp Interface.
2134  * See IMAPIProp_GetProps.
2135  */
2136 static HRESULT WINAPI
2137 IPropData_fnGetProps(LPPROPDATA iface, LPSPropTagArray lpPropTags,
2138                      ULONG ulFlags, ULONG *lpCount, LPSPropValue *lppProps)
2139 {
2140     return IMAPIProp_fnGetProps((LPMAPIPROP)iface, lpPropTags, ulFlags,
2141                                 lpCount, lppProps);
2142 }
2143
2144 /**************************************************************************
2145  *  IPropData_GetPropList {MAPI32}
2146  *
2147  * Inherited method from the IMAPIProp Interface.
2148  * See IMAPIProp_GetPropList.
2149  */
2150 static HRESULT WINAPI
2151 IPropData_fnGetPropList(LPPROPDATA iface, ULONG ulFlags,
2152                                               LPSPropTagArray *lppPropTags)
2153 {
2154     return IMAPIProp_fnGetPropList((LPMAPIPROP)iface, ulFlags, lppPropTags);
2155 }
2156
2157 /**************************************************************************
2158  *  IPropData_OpenProperty {MAPI32}
2159  *
2160  * Inherited method from the IMAPIProp Interface.
2161  * See IMAPIProp_OpenProperty.
2162  */
2163 static HRESULT WINAPI
2164 IPropData_fnOpenProperty(LPPROPDATA iface, ULONG ulPropTag, LPCIID iid,
2165                          ULONG ulOpts, ULONG ulFlags, LPUNKNOWN *lpUnk)
2166 {
2167     return IMAPIProp_fnOpenProperty((LPMAPIPROP)iface, ulPropTag, iid,
2168                                     ulOpts, ulFlags, lpUnk);
2169 }
2170
2171 /**************************************************************************
2172  *  IPropData_SetProps {MAPI32}
2173  *
2174  * Inherited method from the IMAPIProp Interface.
2175  * See IMAPIProp_SetProps.
2176  */
2177 static HRESULT WINAPI
2178 IPropData_fnSetProps(LPPROPDATA iface, ULONG cValues, LPSPropValue lpProps,
2179                      LPSPropProblemArray *lppProbs)
2180 {
2181     return IMAPIProp_fnSetProps((LPMAPIPROP)iface, cValues, lpProps, lppProbs);
2182 }
2183
2184 /**************************************************************************
2185  *  IPropData_DeleteProps {MAPI32}
2186  *
2187  * Inherited method from the IMAPIProp Interface.
2188  * See IMAPIProp_DeleteProps.
2189  */
2190 static HRESULT WINAPI
2191 IPropData_fnDeleteProps(LPPROPDATA iface, LPSPropTagArray lpPropTags,
2192                         LPSPropProblemArray *lppProbs)
2193 {
2194     return IMAPIProp_fnDeleteProps((LPMAPIPROP)iface, lpPropTags, lppProbs);
2195 }
2196
2197 /**************************************************************************
2198  *  IPropData_CopyTo {MAPI32}
2199  *
2200  * Inherited method from the IMAPIProp Interface.
2201  * See IMAPIProp_CopyTo.
2202  */
2203 static HRESULT WINAPI
2204 IPropData_fnCopyTo(LPPROPDATA iface, ULONG ciidExclude, LPCIID lpIid,
2205                    LPSPropTagArray lpProps, ULONG ulParam,
2206                    LPMAPIPROGRESS lpProgress, LPCIID lpIface, LPVOID lpDst,
2207                    ULONG ulFlags, LPSPropProblemArray *lppProbs)
2208 {
2209     return IMAPIProp_fnCopyTo((LPMAPIPROP)iface, ciidExclude, lpIid, lpProps,
2210                               ulParam, lpProgress, lpIface, lpDst,
2211                               ulFlags, lppProbs);
2212 }
2213
2214 /**************************************************************************
2215  *  IPropData_CopyProps {MAPI32}
2216  *
2217  * Inherited method from the IMAPIProp Interface.
2218  * See IMAPIProp_CopyProps.
2219  */
2220 static HRESULT WINAPI
2221 IPropData_fnCopyProps(LPPROPDATA iface, LPSPropTagArray lpProps,
2222                       ULONG ulParam, LPMAPIPROGRESS lpProgress, LPCIID lpIface,
2223                       LPVOID lpDst, ULONG ulFlags, LPSPropProblemArray *lppProbs)
2224 {
2225     return IMAPIProp_fnCopyProps((LPMAPIPROP)iface, lpProps, ulParam,
2226                                  lpProgress, lpIface, lpDst, ulFlags, lppProbs);
2227 }
2228
2229 /**************************************************************************
2230  *  IPropData_GetNamesFromIDs {MAPI32}
2231  *
2232  * Inherited method from the IMAPIProp Interface.
2233  * See IMAPIProp_GetNamesFromIDs.
2234  */
2235 static HRESULT WINAPI
2236 IPropData_fnGetNamesFromIDs(LPPROPDATA iface, LPSPropTagArray *lppPropTags,
2237                             LPGUID iid, ULONG ulFlags, ULONG *lpCount,
2238                             LPMAPINAMEID **lpppNames)
2239 {
2240     return IMAPIProp_fnGetNamesFromIDs((LPMAPIPROP)iface, lppPropTags, iid,
2241                                        ulFlags, lpCount, lpppNames);
2242 }
2243
2244 /**************************************************************************
2245  *  IPropData_GetIDsFromNames {MAPI32}
2246  *
2247  * Inherited method from the IMAPIProp Interface.
2248  * See IMAPIProp_GetIDsFromNames.
2249  */
2250 static HRESULT WINAPI
2251 IPropData_fnGetIDsFromNames(LPPROPDATA iface, ULONG ulNames,
2252                             LPMAPINAMEID *lppNames, ULONG ulFlags,
2253                             LPSPropTagArray *lppPropTags)
2254 {
2255     return IMAPIProp_fnGetIDsFromNames((LPMAPIPROP)iface, ulNames, lppNames,
2256                                        ulFlags, lppPropTags);
2257 }
2258
2259 /**************************************************************************
2260  *  IPropData_HrSetObjAccess {MAPI32}
2261  *
2262  * Set the access level of an IPropData object.
2263  *
2264  * PARAMS
2265  *  iface    [I] IPropData object to set the access on
2266  *  ulAccess [I] Either IPROP_READONLY or IPROP_READWRITE for read or
2267  *               read/write access respectively.
2268  *
2269  * RETURNS
2270  *  Success: S_OK. The objects access level is changed.
2271  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
2272  */
2273 static HRESULT WINAPI
2274 IPropData_fnHrSetObjAccess(LPPROPDATA iface, ULONG ulAccess)
2275 {
2276     IPropDataImpl *This = (IPropDataImpl*)iface;
2277
2278     TRACE("(%p,%x)\n", iface, ulAccess);
2279
2280     if (!iface || ulAccess < IPROP_READONLY || ulAccess > IPROP_READWRITE)
2281         return MAPI_E_INVALID_PARAMETER;
2282
2283     IMAPIPROP_Lock(This);
2284
2285     This->ulObjAccess = ulAccess;
2286
2287     IMAPIPROP_Unlock(This);
2288     return S_OK;
2289 }
2290
2291 /* Internal - determine if an access value is bad */
2292 static inline BOOL PROP_IsBadAccess(ULONG ulAccess)
2293 {
2294     switch (ulAccess)
2295     {
2296     case IPROP_READONLY|IPROP_CLEAN:
2297     case IPROP_READONLY|IPROP_DIRTY:
2298     case IPROP_READWRITE|IPROP_CLEAN:
2299     case IPROP_READWRITE|IPROP_DIRTY:
2300         return FALSE;
2301     }
2302     return TRUE;
2303 }
2304
2305 /**************************************************************************
2306  *  IPropData_HrSetPropAccess {MAPI32}
2307  *
2308  * Set the access levels for a group of property values in an IPropData object.
2309  *
2310  * PARAMS
2311  *  iface    [I] IPropData object to set access levels in.
2312  *  lpTags   [I] List of property Id's to set access for.
2313  *  lpAccess [O] Access level for each property in lpTags.
2314  *
2315  * RETURNS
2316  *  Success: S_OK. The access level of each property value in lpTags that is
2317  *           present in iface is changed.
2318  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
2319  *
2320  * NOTES
2321  *  - Each access level in lpAccess must contain at least one of IPROP_READONLY
2322  *    or IPROP_READWRITE, but not both, and also IPROP_CLEAN or IPROP_DIRTY,
2323  *    but not both. No other bits should be set.
2324  *  - If a property Id in lpTags is not present in iface, it is ignored.
2325  */
2326 static HRESULT WINAPI
2327 IPropData_fnHrSetPropAccess(LPPROPDATA iface, LPSPropTagArray lpTags,
2328                             ULONG *lpAccess)
2329 {
2330     IPropDataImpl *This = (IPropDataImpl*)iface;
2331
2332     ULONG i;
2333
2334     TRACE("(%p,%p,%p)\n", iface, lpTags, lpAccess);
2335
2336     if (!iface || !lpTags || !lpAccess)
2337         return MAPI_E_INVALID_PARAMETER;
2338
2339     for (i = 0; i < lpTags->cValues; i++)
2340     {
2341         if (FBadPropTag(lpTags->aulPropTag[i]) || PROP_IsBadAccess(lpAccess[i]))
2342             return MAPI_E_INVALID_PARAMETER;
2343     }
2344
2345     IMAPIPROP_Lock(This);
2346
2347     for (i = 0; i < lpTags->cValues; i++)
2348     {
2349         LPIPropDataItem item = IMAPIPROP_GetValue(This, lpTags->aulPropTag[i]);
2350
2351         if (item)
2352             item->ulAccess = lpAccess[i];
2353     }
2354
2355     IMAPIPROP_Unlock(This);
2356     return S_OK;
2357 }
2358
2359 /**************************************************************************
2360  *  IPropData_HrGetPropAccess {MAPI32}
2361  *
2362  * Get the access levels for a group of property values in an IPropData object.
2363  *
2364  * PARAMS
2365  *  iface     [I] IPropData object to get access levels from.
2366  *  lppTags   [O] Destination for the list of property Id's in iface.
2367  *  lppAccess [O] Destination for access level for each property in lppTags.
2368  *
2369  * RETURNS
2370  *  Success: S_OK. lppTags and lppAccess contain the property Id's and the
2371  *           Access level of each property value in iface.
2372  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid, or
2373  *           MAPI_E_NOT_ENOUGH_MEMORY if memory allocation fails.
2374  *
2375  * NOTES
2376  *  - *lppTags and *lppAccess should be freed with MAPIFreeBuffer() by the caller.
2377  */
2378 static HRESULT WINAPI
2379 IPropData_fnHrGetPropAccess(LPPROPDATA iface, LPSPropTagArray *lppTags,
2380                             ULONG **lppAccess)
2381 {
2382     IPropDataImpl *This = (IPropDataImpl*)iface;
2383     LPVOID lpMem;
2384     HRESULT hRet;
2385     ULONG i;
2386
2387     TRACE("(%p,%p,%p) stub\n", iface, lppTags, lppAccess);
2388
2389     if (!iface || !lppTags || !lppAccess)
2390         return MAPI_E_INVALID_PARAMETER;
2391
2392     *lppTags = NULL;
2393     *lppAccess = NULL;
2394
2395     IMAPIPROP_Lock(This);
2396
2397     hRet = This->lpAlloc(CbNewSPropTagArray(This->ulNumValues), &lpMem);
2398     if (SUCCEEDED(hRet))
2399     {
2400         *lppTags = lpMem;
2401
2402         hRet = This->lpAlloc(This->ulNumValues * sizeof(ULONG), &lpMem);
2403         if (SUCCEEDED(hRet))
2404         {
2405             struct list *cursor;
2406
2407             *lppAccess = lpMem;
2408             (*lppTags)->cValues = This->ulNumValues;
2409
2410             i = 0;
2411             LIST_FOR_EACH(cursor, &This->values)
2412             {
2413                 LPIPropDataItem item = LIST_ENTRY(cursor, IPropDataItem, entry);
2414                 (*lppTags)->aulPropTag[i] = item->value->ulPropTag;
2415                 (*lppAccess)[i] = item->ulAccess;
2416                 i++;
2417             }
2418             IMAPIPROP_Unlock(This);
2419             return S_OK;
2420         }
2421         This->lpFree(*lppTags);
2422         *lppTags = 0;
2423     }
2424     IMAPIPROP_Unlock(This);
2425     return MAPI_E_NOT_ENOUGH_MEMORY;
2426 }
2427
2428 /**************************************************************************
2429  *  IPropData_HrAddObjProps {MAPI32}
2430  *
2431  * Not documented at this time.
2432  *
2433  * RETURNS
2434  *  An HRESULT success/failure code.
2435  */
2436 static HRESULT WINAPI
2437 IPropData_fnHrAddObjProps(LPPROPDATA iface, LPSPropTagArray lpTags,
2438                           LPSPropProblemArray *lppProbs)
2439 {
2440 #if 0
2441     ULONG i;
2442     HRESULT hRet;
2443     LPSPropValue lpValues;
2444 #endif
2445
2446     FIXME("(%p,%p,%p) stub\n", iface, lpTags, lppProbs);
2447
2448     if (!iface || !lpTags)
2449         return MAPI_E_INVALID_PARAMETER;
2450
2451     /* FIXME: Below is the obvious implementation, adding all the properties
2452      *        in lpTags to the object. However, it doesn't appear that this
2453      *        is what this function does.
2454      */
2455     return S_OK;
2456 #if 0
2457     if (!lpTags->cValues)
2458         return S_OK;
2459
2460     lpValues = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2461                          lpTags->cValues * sizeof(SPropValue));
2462     if (!lpValues)
2463         return MAPI_E_NOT_ENOUGH_MEMORY;
2464
2465     for (i = 0; i < lpTags->cValues; i++)
2466         lpValues[i].ulPropTag = lpTags->aulPropTag[i];
2467
2468     hRet = IPropData_SetProps(iface, lpTags->cValues, lpValues, lppProbs);
2469     HeapFree(GetProcessHeap(), 0, lpValues);
2470     return hRet;
2471 #endif
2472 }
2473
2474 static const IPropDataVtbl IPropDataImpl_vtbl =
2475 {
2476     IPropData_fnQueryInterface,
2477     IPropData_fnAddRef,
2478     IPropData_fnRelease,
2479     IPropData_fnGetLastError,
2480     IPropData_fnSaveChanges,
2481     IPropData_fnGetProps,
2482     IPropData_fnGetPropList,
2483     IPropData_fnOpenProperty,
2484     IPropData_fnSetProps,
2485     IPropData_fnDeleteProps,
2486     IPropData_fnCopyTo,
2487     IPropData_fnCopyProps,
2488     IPropData_fnGetNamesFromIDs,
2489     IPropData_fnGetIDsFromNames,
2490     IPropData_fnHrSetObjAccess,
2491     IPropData_fnHrSetPropAccess,
2492     IPropData_fnHrGetPropAccess,
2493     IPropData_fnHrAddObjProps
2494 };
2495
2496 /*************************************************************************
2497  * CreateIProp@24 (MAPI32.60)
2498  *
2499  * Create an IPropData object.
2500  *
2501  * PARAMS
2502  *  iid         [I] GUID of the object to create. Use &IID_IMAPIPropData or NULL
2503  *  lpAlloc     [I] Memory allocation function. Use MAPIAllocateBuffer()
2504  *  lpMore      [I] Linked memory allocation function. Use MAPIAllocateMore()
2505  *  lpFree      [I] Memory free function. Use MAPIFreeBuffer()
2506  *  lpReserved  [I] Reserved, set to NULL
2507  *  lppPropData [O] Destination for created IPropData object
2508  *
2509  * RETURNS
2510  *  Success: S_OK. *lppPropData contains the newly created object.
2511  *  Failure: MAPI_E_INTERFACE_NOT_SUPPORTED, if iid is non-NULL and not supported,
2512  *           MAPI_E_INVALID_PARAMETER, if any parameter is invalid
2513  */
2514 SCODE WINAPI CreateIProp(LPCIID iid, ALLOCATEBUFFER *lpAlloc,
2515                          ALLOCATEMORE *lpMore, FREEBUFFER *lpFree,
2516                          LPVOID lpReserved, LPPROPDATA *lppPropData)
2517 {
2518     IPropDataImpl *lpPropData;
2519     SCODE scode;
2520
2521     TRACE("(%s,%p,%p,%p,%p,%p)\n", debugstr_guid(iid), lpAlloc, lpMore, lpFree,
2522           lpReserved, lppPropData);
2523
2524     if (lppPropData)
2525         *lppPropData = NULL;
2526
2527     if (iid && !IsEqualGUID(iid, &IID_IMAPIPropData))
2528         return MAPI_E_INTERFACE_NOT_SUPPORTED;
2529
2530     if (!lpAlloc || !lpMore || !lpFree || lpReserved || !lppPropData)
2531         return MAPI_E_INVALID_PARAMETER;
2532
2533     scode = lpAlloc(sizeof(IPropDataImpl), (LPVOID*)&lpPropData);
2534
2535     if (SUCCEEDED(scode))
2536     {
2537         lpPropData->lpVtbl = &IPropDataImpl_vtbl;
2538         lpPropData->lRef = 1;
2539         lpPropData->lpAlloc = lpAlloc;
2540         lpPropData->lpMore = lpMore;
2541         lpPropData->lpFree = lpFree;
2542         lpPropData->ulObjAccess = IPROP_READWRITE;
2543         lpPropData->ulNumValues = 0;
2544         list_init(&lpPropData->values);
2545         InitializeCriticalSection(&lpPropData->cs);
2546         lpPropData->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IPropDataImpl.cs");
2547         *lppPropData = (LPPROPDATA)lpPropData;
2548     }
2549     return scode;
2550 }