msxml3: Fix a compile error without xml headers.
[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 WINAPI
1489 IMAPIProp_fnQueryInterface(LPMAPIPROP iface, REFIID riid, LPVOID *ppvObj)
1490 {
1491     IPropDataImpl *This = (IPropDataImpl*)iface;
1492
1493     TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppvObj);
1494
1495     if (!ppvObj || !riid)
1496         return MAPI_E_INVALID_PARAMETER;
1497
1498     *ppvObj = NULL;
1499
1500     if(IsEqualIID(riid, &IID_IUnknown) ||
1501        IsEqualIID(riid, &IID_IMAPIProp) ||
1502        IsEqualIID(riid, &IID_IMAPIPropData))
1503     {
1504         *ppvObj = This;
1505         IPropData_AddRef(iface);
1506         TRACE("returning %p\n", *ppvObj);
1507         return S_OK;
1508     }
1509
1510     TRACE("returning E_NOINTERFACE\n");
1511     return MAPI_E_INTERFACE_NOT_SUPPORTED;
1512 }
1513
1514 /**************************************************************************
1515  *  IMAPIProp_AddRef {MAPI32}
1516  *
1517  * Inherited method from the IUnknown Interface.
1518  * See IUnknown_AddRef.
1519  */
1520 static inline ULONG WINAPI IMAPIProp_fnAddRef(LPMAPIPROP iface)
1521 {
1522     IPropDataImpl *This = (IPropDataImpl*)iface;
1523
1524     TRACE("(%p)->(count before=%u)\n", This, This->lRef);
1525
1526     return InterlockedIncrement(&This->lRef);
1527 }
1528
1529 /**************************************************************************
1530  *  IMAPIProp_Release {MAPI32}
1531  *
1532  * Inherited method from the IUnknown Interface.
1533  * See IUnknown_Release.
1534  */
1535 static inline ULONG WINAPI IMAPIProp_fnRelease(LPMAPIPROP iface)
1536 {
1537     IPropDataImpl *This = (IPropDataImpl*)iface;
1538     LONG lRef;
1539
1540     TRACE("(%p)->(count before=%u)\n", This, This->lRef);
1541
1542     lRef = InterlockedDecrement(&This->lRef);
1543     if (!lRef)
1544     {
1545         TRACE("Destroying IPropData (%p)\n",This);
1546
1547         /* Note: No need to lock, since no other thread is referencing iface */
1548         while (!list_empty(&This->values))
1549         {
1550             struct list *head = list_head(&This->values);
1551             LPIPropDataItem current = LIST_ENTRY(head, IPropDataItem, entry);
1552             list_remove(head);
1553             This->lpFree(current->value);
1554             This->lpFree(current);
1555         }
1556         This->cs.DebugInfo->Spare[0] = 0;
1557         DeleteCriticalSection(&This->cs);
1558         This->lpFree(This);
1559     }
1560     return (ULONG)lRef;
1561 }
1562
1563 /**************************************************************************
1564  *  IMAPIProp_GetLastError {MAPI32}
1565  *
1566  * Get information about the last error that occurred in an IMAPIProp object.
1567  *
1568  * PARAMS
1569  *  iface    [I] IMAPIProp object that experienced the error
1570  *  hRes     [I] Result of the call that returned an error
1571  *  ulFlags  [I] 0=return Ascii strings, MAPI_UNICODE=return Unicode strings
1572  *  lppError [O] Destination for detailed error information
1573  *
1574  * RETURNS
1575  *  Success: S_OK. *lppError contains details about the last error.
1576  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid,
1577  *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails.
1578  *
1579  * NOTES
1580  *  - If this function succeeds, the returned information in *lppError must be
1581  *  freed using MAPIFreeBuffer() once the caller is finished with it.
1582  *  - It is possible for this function to succeed and set *lppError to NULL,
1583  *  if there is no further information to report about hRes.
1584  */
1585 static inline HRESULT WINAPI
1586 IMAPIProp_fnGetLastError(LPMAPIPROP iface, HRESULT hRes,
1587                          ULONG ulFlags, LPMAPIERROR *lppError)
1588 {
1589     TRACE("(%p,0x%08X,0x%08X,%p)\n", iface, hRes, ulFlags, lppError);
1590
1591     if (!lppError  || SUCCEEDED(hRes) || (ulFlags & ~MAPI_UNICODE))
1592         return MAPI_E_INVALID_PARAMETER;
1593
1594     *lppError = NULL;
1595     return S_OK;
1596 }
1597
1598 /**************************************************************************
1599  *  IMAPIProp_SaveChanges {MAPI32}
1600  *
1601  * Update any changes made to a transactional IMAPIProp object.
1602  *
1603  * PARAMS
1604  *  iface    [I] IMAPIProp object to update
1605  *  ulFlags  [I] Flags controlling the update.
1606  *
1607  * RETURNS
1608  *  Success: S_OK. Any outstanding changes are committed to the object.
1609  *  Failure: An HRESULT error code describing the error.
1610  */
1611 static inline HRESULT WINAPI
1612 IMAPIProp_fnSaveChanges(LPMAPIPROP iface, ULONG ulFlags)
1613 {
1614     TRACE("(%p,0x%08X)\n", iface, ulFlags);
1615
1616      /* Since this object is not transacted we do not need to implement this */
1617      /* FIXME: Should we set the access levels to clean? */
1618     return S_OK;
1619 }
1620
1621 /**************************************************************************
1622  *  IMAPIProp_GetProps {MAPI32}
1623  *
1624  * Get property values from an IMAPIProp object.
1625  *
1626  * PARAMS
1627  *  iface    [I] IMAPIProp object to get the property values from
1628  *  lpTags   [I] Property tage of property values to be retrieved
1629  *  ulFlags  [I] Return 0=Ascii MAPI_UNICODE=Unicode strings for
1630  *                 unspecified types
1631  *  lpCount  [O] Destination for number of properties returned
1632  *  lppProps [O] Destination for returned property values
1633  *
1634  * RETURNS
1635  *  Success: S_OK. *lppProps and *lpCount are updated.
1636  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
1637  *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails, or
1638  *           MAPI_W_ERRORS_RETURNED if not all properties were retrieved
1639  *           successfully.
1640  * NOTES
1641  *  - If MAPI_W_ERRORS_RETURNED is returned, any properties that could not be
1642  *    retrieved from iface are present in lppProps with their type
1643  *    changed to PT_ERROR and Id unchanged.
1644  */
1645 static inline HRESULT WINAPI
1646 IMAPIProp_fnGetProps(LPMAPIPROP iface, LPSPropTagArray lpTags,
1647                      ULONG ulFlags, ULONG *lpCount, LPSPropValue *lppProps)
1648 {
1649     ULONG i;
1650     HRESULT hRet = S_OK;
1651     IPropDataImpl *This = (IPropDataImpl*)iface;
1652
1653     TRACE("(%p,%p,0x%08x,%p,%p) stub\n", iface, lpTags, ulFlags,
1654           lpCount, lppProps);
1655
1656     if (!iface || ulFlags & ~MAPI_UNICODE || !lpTags || *lpCount || !lppProps)
1657         return MAPI_E_INVALID_PARAMETER;
1658
1659     FIXME("semi-stub, flags not supported\n");
1660
1661     *lpCount = lpTags->cValues;
1662     *lppProps = NULL;
1663
1664     if (*lpCount)
1665     {
1666         hRet = MAPIAllocateBuffer(*lpCount * sizeof(SPropValue), (LPVOID*)lppProps);
1667         if (FAILED(hRet))
1668             return hRet;
1669
1670         IMAPIPROP_Lock(This);
1671
1672         for (i = 0; i < lpTags->cValues; i++)
1673         {
1674             HRESULT hRetTmp = E_INVALIDARG;
1675             LPIPropDataItem item;
1676
1677             item = IMAPIPROP_GetValue(This, lpTags->aulPropTag[i]);
1678
1679             if (item)
1680                 hRetTmp = PropCopyMore(&(*lppProps)[i], item->value,
1681                                        This->lpMore, *lppProps);
1682             if (FAILED(hRetTmp))
1683             {
1684                 hRet = MAPI_W_ERRORS_RETURNED;
1685                 (*lppProps)[i].ulPropTag =
1686                     CHANGE_PROP_TYPE(lpTags->aulPropTag[i], PT_ERROR);
1687             }
1688         }
1689
1690         IMAPIPROP_Unlock(This);
1691     }
1692     return hRet;
1693 }
1694
1695 /**************************************************************************
1696  *  MAPIProp_GetPropList {MAPI32}
1697  *
1698  * Get the list of property tags for all values in an IMAPIProp object.
1699  *
1700  * PARAMS
1701  *  iface   [I] IMAPIProp object to get the property tag list from
1702  *  ulFlags [I] Return 0=Ascii MAPI_UNICODE=Unicode strings for
1703  *              unspecified types
1704  *  lppTags [O] Destination for the retrieved property tag list
1705  *
1706  * RETURNS
1707  *  Success: S_OK. *lppTags contains the tags for all available properties.
1708  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
1709  *           MAPI_E_BAD_CHARWIDTH, if Ascii or Unicode strings are requested
1710  *           and that type of string is not supported.
1711  */
1712 static inline HRESULT WINAPI
1713 IMAPIProp_fnGetPropList(LPMAPIPROP iface, ULONG ulFlags,
1714                         LPSPropTagArray *lppTags)
1715 {
1716     IPropDataImpl *This = (IPropDataImpl*)iface;
1717     ULONG i;
1718     HRESULT hRet;
1719
1720     TRACE("(%p,0x%08x,%p) stub\n", iface, ulFlags, lppTags);
1721
1722     if (!iface || ulFlags & ~MAPI_UNICODE || !lppTags)
1723         return MAPI_E_INVALID_PARAMETER;
1724
1725     FIXME("semi-stub, flags not supported\n");
1726
1727     *lppTags = NULL;
1728
1729     IMAPIPROP_Lock(This);
1730
1731     hRet = MAPIAllocateBuffer(CbNewSPropTagArray(This->ulNumValues),
1732                               (LPVOID*)lppTags);
1733     if (SUCCEEDED(hRet))
1734     {
1735         struct list *cursor;
1736
1737         i = 0;
1738         LIST_FOR_EACH(cursor, &This->values)
1739         {
1740             LPIPropDataItem current = LIST_ENTRY(cursor, IPropDataItem, entry);
1741             (*lppTags)->aulPropTag[i] = current->value->ulPropTag;
1742             i++;
1743         }
1744         (*lppTags)->cValues = This->ulNumValues;
1745     }
1746
1747     IMAPIPROP_Unlock(This);
1748     return hRet;
1749 }
1750
1751 /**************************************************************************
1752  *  IMAPIProp_OpenProperty {MAPI32}
1753  *
1754  * Not documented at this time.
1755  *
1756  * RETURNS
1757  *  An HRESULT success/failure code.
1758  */
1759 static inline HRESULT WINAPI
1760 IMAPIProp_fnOpenProperty(LPMAPIPROP iface, ULONG ulPropTag, LPCIID iid,
1761                          ULONG ulOpts, ULONG ulFlags, LPUNKNOWN *lpUnk)
1762 {
1763     FIXME("(%p,%u,%s,%u,0x%08x,%p) stub\n", iface, ulPropTag,
1764           debugstr_guid(iid), ulOpts, ulFlags, lpUnk);
1765     return MAPI_E_NO_SUPPORT;
1766 }
1767
1768
1769 /**************************************************************************
1770  *  IMAPIProp_SetProps {MAPI32}
1771  *
1772  * Add or edit the property values in an IMAPIProp object.
1773  *
1774  * PARAMS
1775  *  iface    [I] IMAPIProp object to get the property tag list from
1776  *  ulValues [I] Number of properties in lpProps
1777  *  lpProps  [I] Property values to set
1778  *  lppProbs [O] Optional destination for any problems that occurred
1779  *
1780  * RETURNS
1781  *  Success: S_OK. The properties in lpProps are added to iface if they don't
1782  *           exist, or changed to the values in lpProps if they do
1783  *  Failure: An HRESULT error code describing the error
1784  */
1785 static inline HRESULT WINAPI
1786 IMAPIProp_fnSetProps(LPMAPIPROP iface, ULONG ulValues,
1787                      LPSPropValue lpProps, LPSPropProblemArray *lppProbs)
1788 {
1789     IPropDataImpl *This = (IPropDataImpl*)iface;
1790     HRESULT hRet = S_OK;
1791     ULONG i;
1792
1793     TRACE("(%p,%u,%p,%p)\n", iface, ulValues, lpProps, lppProbs);
1794
1795     if (!iface || !lpProps)
1796       return MAPI_E_INVALID_PARAMETER;
1797
1798     for (i = 0; i < ulValues; i++)
1799     {
1800         if (FBadProp(&lpProps[i]) ||
1801             PROP_TYPE(lpProps[i].ulPropTag) == PT_OBJECT ||
1802             PROP_TYPE(lpProps[i].ulPropTag) == PT_NULL)
1803           return MAPI_E_INVALID_PARAMETER;
1804     }
1805
1806     IMAPIPROP_Lock(This);
1807
1808     /* FIXME: Under what circumstances is lpProbs created? */
1809     for (i = 0; i < ulValues; i++)
1810     {
1811         LPIPropDataItem item = IMAPIPROP_GetValue(This, lpProps[i].ulPropTag);
1812
1813         if (item)
1814         {
1815             HRESULT hRetTmp;
1816             LPVOID lpMem = NULL;
1817
1818             /* Found, so update the existing value */
1819             if (item->value->ulPropTag != lpProps[i].ulPropTag)
1820                 FIXME("semi-stub, overwriting type (not coercing)\n");
1821
1822             hRetTmp = This->lpAlloc(sizeof(SPropValue), &lpMem);
1823             if (SUCCEEDED(hRetTmp))
1824             {
1825                 hRetTmp = PropCopyMore(lpMem, &lpProps[i], This->lpMore, lpMem);
1826                 if (SUCCEEDED(hRetTmp))
1827                 {
1828                     This->lpFree(item->value);
1829                     item->value = lpMem;
1830                     continue;
1831                 }
1832                 This->lpFree(lpMem);
1833             }
1834             hRet = hRetTmp;
1835         }
1836         else
1837         {
1838             /* Add new value */
1839             if (!(item = IMAPIPROP_AddValue(This, &lpProps[i])))
1840                 hRet = MAPI_E_NOT_ENOUGH_MEMORY;
1841         }
1842     }
1843
1844     IMAPIPROP_Unlock(This);
1845     return hRet;
1846 }
1847
1848 /**************************************************************************
1849  *  IMAPIProp_DeleteProps {MAPI32}
1850  *
1851  * Delete one or more property values from an IMAPIProp object.
1852  *
1853  * PARAMS
1854  *  iface    [I] IMAPIProp object to remove property values from.
1855  *  lpTags   [I] Collection of property Id's to remove from iface.
1856  *  lppProbs [O] Destination for problems encountered, if any.
1857  *
1858  * RETURNS
1859  *  Success: S_OK. Any properties in iface matching property Id's in lpTags have
1860  *           been deleted. If lppProbs is non-NULL it contains details of any
1861  *           errors that occurred.
1862  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
1863  *           E_ACCESSDENIED, if this object was created using CreateIProp() and
1864  *           a subsequent call to IPropData_SetObjAcess() was made specifying
1865  *           IPROP_READONLY as the access type.
1866  *
1867  * NOTES
1868  *  - lppProbs will not be populated for cases where a property Id is present
1869  *    in lpTags but not in iface.
1870  *  - lppProbs should be deleted with MAPIFreeBuffer() if returned.
1871  */
1872 static inline HRESULT WINAPI
1873 IMAPIProp_fnDeleteProps(LPMAPIPROP iface, LPSPropTagArray lpTags,
1874                         LPSPropProblemArray *lppProbs)
1875 {
1876     IPropDataImpl *This = (IPropDataImpl*)iface;
1877     ULONG i, numProbs = 0;
1878     HRESULT hRet = S_OK;
1879
1880     TRACE("(%p,%p,%p)\n", iface, lpTags, lppProbs);
1881
1882     if (!iface || !lpTags)
1883         return MAPI_E_INVALID_PARAMETER;
1884
1885     if (lppProbs)
1886         *lppProbs = NULL;
1887
1888     for (i = 0; i < lpTags->cValues; i++)
1889     {
1890         if (FBadPropTag(lpTags->aulPropTag[i]) ||
1891             PROP_TYPE(lpTags->aulPropTag[i]) == PT_OBJECT ||
1892             PROP_TYPE(lpTags->aulPropTag[i]) == PT_NULL)
1893           return MAPI_E_INVALID_PARAMETER;
1894     }
1895
1896     IMAPIPROP_Lock(This);
1897
1898     if (This->ulObjAccess != IPROP_READWRITE)
1899     {
1900         IMAPIPROP_Unlock(This);
1901         return E_ACCESSDENIED;
1902     }
1903
1904     for (i = 0; i < lpTags->cValues; i++)
1905     {
1906         LPIPropDataItem item = IMAPIPROP_GetValue(This, lpTags->aulPropTag[i]);
1907
1908         if (item)
1909         {
1910             if (item->ulAccess & IPROP_READWRITE)
1911             {
1912                 /* Everything hunky-dory, remove the item */
1913                 list_remove(&item->entry);
1914                 This->lpFree(item->value); /* Also frees value pointers */
1915                 This->lpFree(item);
1916                 This->ulNumValues--;
1917             }
1918             else if (lppProbs)
1919             {
1920                  /* Can't write the value. Create/populate problems array */
1921                  if (!*lppProbs)
1922                  {
1923                      /* Create problems array */
1924                      ULONG ulSize = CbNewSPropProblemArray(lpTags->cValues - i);
1925                      HRESULT hRetTmp = MAPIAllocateBuffer(ulSize, (LPVOID*)lppProbs);
1926                      if (FAILED(hRetTmp))
1927                          hRet = hRetTmp;
1928                  }
1929                  if (*lppProbs)
1930                  {
1931                      LPSPropProblem lpProb = &(*lppProbs)->aProblem[numProbs];
1932                      lpProb->ulIndex = i;
1933                      lpProb->ulPropTag = lpTags->aulPropTag[i];
1934                      lpProb->scode = E_ACCESSDENIED;
1935                      numProbs++;
1936                  }
1937             }
1938         }
1939     }
1940     if (lppProbs && *lppProbs)
1941         (*lppProbs)->cProblem = numProbs;
1942
1943     IMAPIPROP_Unlock(This);
1944     return hRet;
1945 }
1946
1947
1948 /**************************************************************************
1949  *  IMAPIProp_CopyTo {MAPI32}
1950  *
1951  * Not documented at this time.
1952  *
1953  * RETURNS
1954  *  An HRESULT success/failure code.
1955  */
1956 static inline HRESULT WINAPI
1957 IMAPIProp_fnCopyTo(LPMAPIPROP iface, ULONG niids, LPCIID lpiidExcl,
1958                    LPSPropTagArray lpPropsExcl, ULONG ulParam,
1959                    LPMAPIPROGRESS lpIProgress, LPCIID lpIfaceIid, LPVOID lpDstObj,
1960                    ULONG ulFlags, LPSPropProblemArray *lppProbs)
1961 {
1962     FIXME("(%p,%u,%p,%p,%x,%p,%s,%p,0x%08X,%p) stub\n", iface, niids,
1963           lpiidExcl, lpPropsExcl, ulParam, lpIProgress,
1964           debugstr_guid(lpIfaceIid), lpDstObj, ulFlags, lppProbs);
1965     return MAPI_E_NO_SUPPORT;
1966 }
1967
1968 /**************************************************************************
1969  *  IMAPIProp_CopyProps {MAPI32}
1970  *
1971  * Not documented at this time.
1972  *
1973  * RETURNS
1974  *  An HRESULT success/failure code.
1975  */
1976 static inline HRESULT WINAPI
1977 IMAPIProp_fnCopyProps(LPMAPIPROP iface, LPSPropTagArray lpInclProps,
1978                       ULONG ulParam, LPMAPIPROGRESS lpIProgress, LPCIID lpIface,
1979                       LPVOID lpDstObj, ULONG ulFlags,
1980                       LPSPropProblemArray *lppProbs)
1981 {
1982     FIXME("(%p,%p,%x,%p,%s,%p,0x%08X,%p) stub\n", iface, lpInclProps,
1983           ulParam, lpIProgress, debugstr_guid(lpIface), lpDstObj, ulFlags,
1984           lppProbs);
1985     return MAPI_E_NO_SUPPORT;
1986 }
1987
1988 /**************************************************************************
1989  *  IMAPIProp_GetNamesFromIDs {MAPI32}
1990  *
1991  * Get the names of properties from their identifiers.
1992  *
1993  * PARAMS
1994  *  iface       [I]   IMAPIProp object to operate on
1995  *  lppPropTags [I/O] Property identifiers to get the names for, or NULL to
1996  *                    get all names
1997  *  iid         [I]   Property set identifier, or NULL
1998  *  ulFlags     [I]   MAPI_NO_IDS=Don't return numeric named properties,
1999  *                    or MAPI_NO_STRINGS=Don't return strings
2000  *  lpCount     [O]   Destination for number of properties returned
2001  *  lpppNames   [O]   Destination for returned names
2002  *
2003  * RETURNS
2004  *  Success: S_OK. *lppPropTags and lpppNames contain the returned
2005  *           name/identifiers.
2006  *  Failure: MAPI_E_NO_SUPPORT, if the object does not support named properties,
2007  *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails, or
2008  *           MAPI_W_ERRORS_RETURNED if not all properties were retrieved
2009  *           successfully.
2010  */
2011 static inline HRESULT WINAPI
2012 IMAPIProp_fnGetNamesFromIDs(LPMAPIPROP iface, LPSPropTagArray *lppPropTags,
2013                             LPGUID iid, ULONG ulFlags, ULONG *lpCount,
2014                             LPMAPINAMEID **lpppNames)
2015 {
2016     FIXME("(%p,%p,%s,0x%08X,%p,%p) stub\n", iface, lppPropTags,
2017           debugstr_guid(iid), ulFlags, lpCount, lpppNames);
2018     return MAPI_E_NO_SUPPORT;
2019 }
2020
2021 /**************************************************************************
2022  *  IMAPIProp_GetIDsFromNames {MAPI32}
2023  *
2024  * Get property identifiers associated with one or more named properties.
2025  *
2026  * PARAMS
2027  *  iface       [I] IMAPIProp object to operate on
2028  *  ulNames     [I] Number of names in lppNames
2029  *  lppNames    [I] Names to query or create, or NULL to query all names
2030  *  ulFlags     [I] Pass MAPI_CREATE to create new named properties
2031  *  lppPropTags [O] Destination for queried or created property identifiers
2032  *
2033  * RETURNS
2034  *  Success: S_OK. *lppPropTags contains the property tags created or requested.
2035  *  Failure: MAPI_E_NO_SUPPORT, if the object does not support named properties,
2036  *           MAPI_E_TOO_BIG, if the object cannot process the number of
2037  *           properties involved.
2038  *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails, or
2039  *           MAPI_W_ERRORS_RETURNED if not all properties were retrieved
2040  *           successfully.
2041  */
2042 static inline HRESULT WINAPI
2043 IMAPIProp_fnGetIDsFromNames(LPMAPIPROP iface, ULONG ulNames,
2044                             LPMAPINAMEID *lppNames, ULONG ulFlags,
2045                             LPSPropTagArray *lppPropTags)
2046 {
2047     FIXME("(%p,%d,%p,0x%08X,%p) stub\n",
2048           iface, ulNames, lppNames, ulFlags, lppPropTags);
2049     return MAPI_E_NO_SUPPORT;
2050 }
2051
2052 /**************************************************************************
2053  *  IPropData {MAPI32}
2054  *
2055  * A default Mapi interface to provide manipulation of object properties.
2056  *
2057  * DESCRIPTION
2058  *  This object provides a default interface suitable in some cases as an
2059  *  implementation of the IMAPIProp interface (which has no default
2060  *  implementation). In addition to the IMAPIProp() methods inherited, this
2061  *  interface allows read/write control over access to the object and its
2062  *  individual properties.
2063  *
2064  *  To obtain the default implementation of this interface from Mapi, call
2065  *  CreateIProp().
2066  *
2067  * METHODS
2068  */
2069
2070 /**************************************************************************
2071  *  IPropData_QueryInterface {MAPI32}
2072  *
2073  * Inherited method from the IMAPIProp Interface.
2074  * See IMAPIProp_QueryInterface.
2075  */
2076 static HRESULT WINAPI
2077 IPropData_fnQueryInterface(LPPROPDATA iface, REFIID riid, LPVOID *ppvObj)
2078 {
2079     return IMAPIProp_fnQueryInterface((LPMAPIPROP)iface, riid, ppvObj);
2080 }
2081
2082 /**************************************************************************
2083  *  IPropData_AddRef {MAPI32}
2084  *
2085  * Inherited method from the IMAPIProp Interface.
2086  * See IMAPIProp_AddRef.
2087  */
2088 static ULONG WINAPI
2089 IPropData_fnAddRef(LPPROPDATA iface)
2090 {
2091     return IMAPIProp_fnAddRef((LPMAPIPROP)iface);
2092 }
2093
2094 /**************************************************************************
2095  *  IPropData_Release {MAPI32}
2096  *
2097  * Inherited method from the IMAPIProp Interface.
2098  * See IMAPIProp_Release.
2099  */
2100 static ULONG WINAPI
2101 IPropData_fnRelease(LPPROPDATA iface)
2102 {
2103     return IMAPIProp_fnRelease((LPMAPIPROP)iface);
2104 }
2105
2106 /**************************************************************************
2107  *  IPropData_GetLastError {MAPI32}
2108  *
2109  * Inherited method from the IMAPIProp Interface.
2110  * See IMAPIProp_GetLastError.
2111  */
2112 static HRESULT WINAPI
2113 IPropData_fnGetLastError(LPPROPDATA iface, HRESULT hRes, ULONG ulFlags,
2114                          LPMAPIERROR *lppError)
2115 {
2116     return IMAPIProp_fnGetLastError((LPMAPIPROP)iface, hRes, ulFlags, lppError);
2117 }
2118
2119 /**************************************************************************
2120  *  IPropData_SaveChanges {MAPI32}
2121  *
2122  * Inherited method from the IMAPIProp Interface.
2123  * See IMAPIProp_SaveChanges.
2124  */
2125 static HRESULT WINAPI
2126 IPropData_fnSaveChanges(LPPROPDATA iface, ULONG ulFlags)
2127 {
2128     return IMAPIProp_fnSaveChanges((LPMAPIPROP)iface, ulFlags);
2129 }
2130
2131 /**************************************************************************
2132  *  IPropData_GetProps {MAPI32}
2133  *
2134  * Inherited method from the IMAPIProp Interface.
2135  * See IMAPIProp_GetProps.
2136  */
2137 static HRESULT WINAPI
2138 IPropData_fnGetProps(LPPROPDATA iface, LPSPropTagArray lpPropTags,
2139                      ULONG ulFlags, ULONG *lpCount, LPSPropValue *lppProps)
2140 {
2141     return IMAPIProp_fnGetProps((LPMAPIPROP)iface, lpPropTags, ulFlags,
2142                                 lpCount, lppProps);
2143 }
2144
2145 /**************************************************************************
2146  *  IPropData_GetPropList {MAPI32}
2147  *
2148  * Inherited method from the IMAPIProp Interface.
2149  * See IMAPIProp_GetPropList.
2150  */
2151 static HRESULT WINAPI
2152 IPropData_fnGetPropList(LPPROPDATA iface, ULONG ulFlags,
2153                                               LPSPropTagArray *lppPropTags)
2154 {
2155     return IMAPIProp_fnGetPropList((LPMAPIPROP)iface, ulFlags, lppPropTags);
2156 }
2157
2158 /**************************************************************************
2159  *  IPropData_OpenProperty {MAPI32}
2160  *
2161  * Inherited method from the IMAPIProp Interface.
2162  * See IMAPIProp_OpenProperty.
2163  */
2164 static HRESULT WINAPI
2165 IPropData_fnOpenProperty(LPPROPDATA iface, ULONG ulPropTag, LPCIID iid,
2166                          ULONG ulOpts, ULONG ulFlags, LPUNKNOWN *lpUnk)
2167 {
2168     return IMAPIProp_fnOpenProperty((LPMAPIPROP)iface, ulPropTag, iid,
2169                                     ulOpts, ulFlags, lpUnk);
2170 }
2171
2172 /**************************************************************************
2173  *  IPropData_SetProps {MAPI32}
2174  *
2175  * Inherited method from the IMAPIProp Interface.
2176  * See IMAPIProp_SetProps.
2177  */
2178 static HRESULT WINAPI
2179 IPropData_fnSetProps(LPPROPDATA iface, ULONG cValues, LPSPropValue lpProps,
2180                      LPSPropProblemArray *lppProbs)
2181 {
2182     return IMAPIProp_fnSetProps((LPMAPIPROP)iface, cValues, lpProps, lppProbs);
2183 }
2184
2185 /**************************************************************************
2186  *  IPropData_DeleteProps {MAPI32}
2187  *
2188  * Inherited method from the IMAPIProp Interface.
2189  * See IMAPIProp_DeleteProps.
2190  */
2191 static HRESULT WINAPI
2192 IPropData_fnDeleteProps(LPPROPDATA iface, LPSPropTagArray lpPropTags,
2193                         LPSPropProblemArray *lppProbs)
2194 {
2195     return IMAPIProp_fnDeleteProps((LPMAPIPROP)iface, lpPropTags, lppProbs);
2196 }
2197
2198 /**************************************************************************
2199  *  IPropData_CopyTo {MAPI32}
2200  *
2201  * Inherited method from the IMAPIProp Interface.
2202  * See IMAPIProp_CopyTo.
2203  */
2204 static HRESULT WINAPI
2205 IPropData_fnCopyTo(LPPROPDATA iface, ULONG ciidExclude, LPCIID lpIid,
2206                    LPSPropTagArray lpProps, ULONG ulParam,
2207                    LPMAPIPROGRESS lpProgress, LPCIID lpIface, LPVOID lpDst,
2208                    ULONG ulFlags, LPSPropProblemArray *lppProbs)
2209 {
2210     return IMAPIProp_fnCopyTo((LPMAPIPROP)iface, ciidExclude, lpIid, lpProps,
2211                               ulParam, lpProgress, lpIface, lpDst,
2212                               ulFlags, lppProbs);
2213 }
2214
2215 /**************************************************************************
2216  *  IPropData_CopyProps {MAPI32}
2217  *
2218  * Inherited method from the IMAPIProp Interface.
2219  * See IMAPIProp_CopyProps.
2220  */
2221 static HRESULT WINAPI
2222 IPropData_fnCopyProps(LPPROPDATA iface, LPSPropTagArray lpProps,
2223                       ULONG ulParam, LPMAPIPROGRESS lpProgress, LPCIID lpIface,
2224                       LPVOID lpDst, ULONG ulFlags, LPSPropProblemArray *lppProbs)
2225 {
2226     return IMAPIProp_fnCopyProps((LPMAPIPROP)iface, lpProps, ulParam,
2227                                  lpProgress, lpIface, lpDst, ulFlags, lppProbs);
2228 }
2229
2230 /**************************************************************************
2231  *  IPropData_GetNamesFromIDs {MAPI32}
2232  *
2233  * Inherited method from the IMAPIProp Interface.
2234  * See IMAPIProp_GetNamesFromIDs.
2235  */
2236 static HRESULT WINAPI
2237 IPropData_fnGetNamesFromIDs(LPPROPDATA iface, LPSPropTagArray *lppPropTags,
2238                             LPGUID iid, ULONG ulFlags, ULONG *lpCount,
2239                             LPMAPINAMEID **lpppNames)
2240 {
2241     return IMAPIProp_fnGetNamesFromIDs((LPMAPIPROP)iface, lppPropTags, iid,
2242                                        ulFlags, lpCount, lpppNames);
2243 }
2244
2245 /**************************************************************************
2246  *  IPropData_GetIDsFromNames {MAPI32}
2247  *
2248  * Inherited method from the IMAPIProp Interface.
2249  * See IMAPIProp_GetIDsFromNames.
2250  */
2251 static HRESULT WINAPI
2252 IPropData_fnGetIDsFromNames(LPPROPDATA iface, ULONG ulNames,
2253                             LPMAPINAMEID *lppNames, ULONG ulFlags,
2254                             LPSPropTagArray *lppPropTags)
2255 {
2256     return IMAPIProp_fnGetIDsFromNames((LPMAPIPROP)iface, ulNames, lppNames,
2257                                        ulFlags, lppPropTags);
2258 }
2259
2260 /**************************************************************************
2261  *  IPropData_HrSetObjAccess {MAPI32}
2262  *
2263  * Set the access level of an IPropData object.
2264  *
2265  * PARAMS
2266  *  iface    [I] IPropData object to set the access on
2267  *  ulAccess [I] Either IPROP_READONLY or IPROP_READWRITE for read or
2268  *               read/write access respectively.
2269  *
2270  * RETURNS
2271  *  Success: S_OK. The objects access level is changed.
2272  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
2273  */
2274 static HRESULT WINAPI
2275 IPropData_fnHrSetObjAccess(LPPROPDATA iface, ULONG ulAccess)
2276 {
2277     IPropDataImpl *This = (IPropDataImpl*)iface;
2278
2279     TRACE("(%p,%x)\n", iface, ulAccess);
2280
2281     if (!iface || ulAccess < IPROP_READONLY || ulAccess > IPROP_READWRITE)
2282         return MAPI_E_INVALID_PARAMETER;
2283
2284     IMAPIPROP_Lock(This);
2285
2286     This->ulObjAccess = ulAccess;
2287
2288     IMAPIPROP_Unlock(This);
2289     return S_OK;
2290 }
2291
2292 /* Internal - determine if an access value is bad */
2293 static inline BOOL PROP_IsBadAccess(ULONG ulAccess)
2294 {
2295     switch (ulAccess)
2296     {
2297     case IPROP_READONLY|IPROP_CLEAN:
2298     case IPROP_READONLY|IPROP_DIRTY:
2299     case IPROP_READWRITE|IPROP_CLEAN:
2300     case IPROP_READWRITE|IPROP_DIRTY:
2301         return FALSE;
2302     }
2303     return TRUE;
2304 }
2305
2306 /**************************************************************************
2307  *  IPropData_HrSetPropAccess {MAPI32}
2308  *
2309  * Set the access levels for a group of property values in an IPropData object.
2310  *
2311  * PARAMS
2312  *  iface    [I] IPropData object to set access levels in.
2313  *  lpTags   [I] List of property Id's to set access for.
2314  *  lpAccess [O] Access level for each property in lpTags.
2315  *
2316  * RETURNS
2317  *  Success: S_OK. The access level of each property value in lpTags that is
2318  *           present in iface is changed.
2319  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
2320  *
2321  * NOTES
2322  *  - Each access level in lpAccess must contain at least one of IPROP_READONLY
2323  *    or IPROP_READWRITE, but not both, and also IPROP_CLEAN or IPROP_DIRTY,
2324  *    but not both. No other bits should be set.
2325  *  - If a property Id in lpTags is not present in iface, it is ignored.
2326  */
2327 static HRESULT WINAPI
2328 IPropData_fnHrSetPropAccess(LPPROPDATA iface, LPSPropTagArray lpTags,
2329                             ULONG *lpAccess)
2330 {
2331     IPropDataImpl *This = (IPropDataImpl*)iface;
2332
2333     ULONG i;
2334
2335     TRACE("(%p,%p,%p)\n", iface, lpTags, lpAccess);
2336
2337     if (!iface || !lpTags || !lpAccess)
2338         return MAPI_E_INVALID_PARAMETER;
2339
2340     for (i = 0; i < lpTags->cValues; i++)
2341     {
2342         if (FBadPropTag(lpTags->aulPropTag[i]) || PROP_IsBadAccess(lpAccess[i]))
2343             return MAPI_E_INVALID_PARAMETER;
2344     }
2345
2346     IMAPIPROP_Lock(This);
2347
2348     for (i = 0; i < lpTags->cValues; i++)
2349     {
2350         LPIPropDataItem item = IMAPIPROP_GetValue(This, lpTags->aulPropTag[i]);
2351
2352         if (item)
2353             item->ulAccess = lpAccess[i];
2354     }
2355
2356     IMAPIPROP_Unlock(This);
2357     return S_OK;
2358 }
2359
2360 /**************************************************************************
2361  *  IPropData_HrGetPropAccess {MAPI32}
2362  *
2363  * Get the access levels for a group of property values in an IPropData object.
2364  *
2365  * PARAMS
2366  *  iface     [I] IPropData object to get access levels from.
2367  *  lppTags   [O] Destination for the list of property Id's in iface.
2368  *  lppAccess [O] Destination for access level for each property in lppTags.
2369  *
2370  * RETURNS
2371  *  Success: S_OK. lppTags and lppAccess contain the property Id's and the
2372  *           Access level of each property value in iface.
2373  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid, or
2374  *           MAPI_E_NOT_ENOUGH_MEMORY if memory allocation fails.
2375  *
2376  * NOTES
2377  *  - *lppTags and *lppAccess should be freed with MAPIFreeBuffer() by the caller.
2378  */
2379 static HRESULT WINAPI
2380 IPropData_fnHrGetPropAccess(LPPROPDATA iface, LPSPropTagArray *lppTags,
2381                             ULONG **lppAccess)
2382 {
2383     IPropDataImpl *This = (IPropDataImpl*)iface;
2384     LPVOID lpMem;
2385     HRESULT hRet;
2386     ULONG i;
2387
2388     TRACE("(%p,%p,%p) stub\n", iface, lppTags, lppAccess);
2389
2390     if (!iface || !lppTags || !lppAccess)
2391         return MAPI_E_INVALID_PARAMETER;
2392
2393     *lppTags = NULL;
2394     *lppAccess = NULL;
2395
2396     IMAPIPROP_Lock(This);
2397
2398     hRet = This->lpAlloc(CbNewSPropTagArray(This->ulNumValues), &lpMem);
2399     if (SUCCEEDED(hRet))
2400     {
2401         *lppTags = lpMem;
2402
2403         hRet = This->lpAlloc(This->ulNumValues * sizeof(ULONG), &lpMem);
2404         if (SUCCEEDED(hRet))
2405         {
2406             struct list *cursor;
2407
2408             *lppAccess = lpMem;
2409             (*lppTags)->cValues = This->ulNumValues;
2410
2411             i = 0;
2412             LIST_FOR_EACH(cursor, &This->values)
2413             {
2414                 LPIPropDataItem item = LIST_ENTRY(cursor, IPropDataItem, entry);
2415                 (*lppTags)->aulPropTag[i] = item->value->ulPropTag;
2416                 (*lppAccess)[i] = item->ulAccess;
2417                 i++;
2418             }
2419             IMAPIPROP_Unlock(This);
2420             return S_OK;
2421         }
2422         This->lpFree(*lppTags);
2423         *lppTags = 0;
2424     }
2425     IMAPIPROP_Unlock(This);
2426     return MAPI_E_NOT_ENOUGH_MEMORY;
2427 }
2428
2429 /**************************************************************************
2430  *  IPropData_HrAddObjProps {MAPI32}
2431  *
2432  * Not documented at this time.
2433  *
2434  * RETURNS
2435  *  An HRESULT success/failure code.
2436  */
2437 static HRESULT WINAPI
2438 IPropData_fnHrAddObjProps(LPPROPDATA iface, LPSPropTagArray lpTags,
2439                           LPSPropProblemArray *lppProbs)
2440 {
2441 #if 0
2442     ULONG i;
2443     HRESULT hRet;
2444     LPSPropValue lpValues;
2445 #endif
2446
2447     FIXME("(%p,%p,%p) stub\n", iface, lpTags, lppProbs);
2448
2449     if (!iface || !lpTags)
2450         return MAPI_E_INVALID_PARAMETER;
2451
2452     /* FIXME: Below is the obvious implementation, adding all the properties
2453      *        in lpTags to the object. However, it doesn't appear that this
2454      *        is what this function does.
2455      */
2456     return S_OK;
2457 #if 0
2458     if (!lpTags->cValues)
2459         return S_OK;
2460
2461     lpValues = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2462                          lpTags->cValues * sizeof(SPropValue));
2463     if (!lpValues)
2464         return MAPI_E_NOT_ENOUGH_MEMORY;
2465
2466     for (i = 0; i < lpTags->cValues; i++)
2467         lpValues[i].ulPropTag = lpTags->aulPropTag[i];
2468
2469     hRet = IPropData_SetProps(iface, lpTags->cValues, lpValues, lppProbs);
2470     HeapFree(GetProcessHeap(), 0, lpValues);
2471     return hRet;
2472 #endif
2473 }
2474
2475 static const IPropDataVtbl IPropDataImpl_vtbl =
2476 {
2477     IPropData_fnQueryInterface,
2478     IPropData_fnAddRef,
2479     IPropData_fnRelease,
2480     IPropData_fnGetLastError,
2481     IPropData_fnSaveChanges,
2482     IPropData_fnGetProps,
2483     IPropData_fnGetPropList,
2484     IPropData_fnOpenProperty,
2485     IPropData_fnSetProps,
2486     IPropData_fnDeleteProps,
2487     IPropData_fnCopyTo,
2488     IPropData_fnCopyProps,
2489     IPropData_fnGetNamesFromIDs,
2490     IPropData_fnGetIDsFromNames,
2491     IPropData_fnHrSetObjAccess,
2492     IPropData_fnHrSetPropAccess,
2493     IPropData_fnHrGetPropAccess,
2494     IPropData_fnHrAddObjProps
2495 };
2496
2497 /*************************************************************************
2498  * CreateIProp@24 (MAPI32.60)
2499  *
2500  * Create an IPropData object.
2501  *
2502  * PARAMS
2503  *  iid         [I] GUID of the object to create. Use &IID_IMAPIPropData or NULL
2504  *  lpAlloc     [I] Memory allocation function. Use MAPIAllocateBuffer()
2505  *  lpMore      [I] Linked memory allocation function. Use MAPIAllocateMore()
2506  *  lpFree      [I] Memory free function. Use MAPIFreeBuffer()
2507  *  lpReserved  [I] Reserved, set to NULL
2508  *  lppPropData [O] Destination for created IPropData object
2509  *
2510  * RETURNS
2511  *  Success: S_OK. *lppPropData contains the newly created object.
2512  *  Failure: MAPI_E_INTERFACE_NOT_SUPPORTED, if iid is non-NULL and not supported,
2513  *           MAPI_E_INVALID_PARAMETER, if any parameter is invalid
2514  */
2515 SCODE WINAPI CreateIProp(LPCIID iid, ALLOCATEBUFFER *lpAlloc,
2516                          ALLOCATEMORE *lpMore, FREEBUFFER *lpFree,
2517                          LPVOID lpReserved, LPPROPDATA *lppPropData)
2518 {
2519     IPropDataImpl *lpPropData;
2520     SCODE scode;
2521
2522     TRACE("(%s,%p,%p,%p,%p,%p)\n", debugstr_guid(iid), lpAlloc, lpMore, lpFree,
2523           lpReserved, lppPropData);
2524
2525     if (lppPropData)
2526         *lppPropData = NULL;
2527
2528     if (iid && !IsEqualGUID(iid, &IID_IMAPIPropData))
2529         return MAPI_E_INTERFACE_NOT_SUPPORTED;
2530
2531     if (!lpAlloc || !lpMore || !lpFree || lpReserved || !lppPropData)
2532         return MAPI_E_INVALID_PARAMETER;
2533
2534     scode = lpAlloc(sizeof(IPropDataImpl), (LPVOID*)&lpPropData);
2535
2536     if (SUCCEEDED(scode))
2537     {
2538         lpPropData->lpVtbl = &IPropDataImpl_vtbl;
2539         lpPropData->lRef = 1;
2540         lpPropData->lpAlloc = lpAlloc;
2541         lpPropData->lpMore = lpMore;
2542         lpPropData->lpFree = lpFree;
2543         lpPropData->ulObjAccess = IPROP_READWRITE;
2544         lpPropData->ulNumValues = 0;
2545         list_init(&lpPropData->values);
2546         InitializeCriticalSection(&lpPropData->cs);
2547         lpPropData->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IPropDataImpl.cs");
2548         *lppPropData = (LPPROPDATA)lpPropData;
2549     }
2550     return scode;
2551 }