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