Stub implementation for GetGeoInfo.
[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 contiguous 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;
799     int iter;
800
801     TRACE("(%d,%p,%p,%p)\n", cValues, lpProps, lpDst, lpCount);
802
803     if (!lpProps || cValues < 0 || !lpDest)
804         return MAPI_E_INVALID_PARAMETER;
805
806     memcpy(lpDst, lpProps, cValues * sizeof(SPropValue));
807
808     for (iter = 0; iter < cValues; iter++)
809     {
810         switch (PROP_TYPE(lpProps->ulPropTag))
811         {
812         case PT_CLSID:
813             lpDest->Value.lpguid = (LPGUID)lpDataDest;
814             memcpy(lpDest->Value.lpguid, lpProps->Value.lpguid, sizeof(GUID));
815             lpDataDest += sizeof(GUID);
816             break;
817         case PT_STRING8:
818             ulLen = lstrlenA(lpProps->Value.lpszA) + 1u;
819             lpDest->Value.lpszA = lpDataDest;
820             memcpy(lpDest->Value.lpszA, lpProps->Value.lpszA, ulLen);
821             lpDataDest += ulLen;
822             break;
823         case PT_UNICODE:
824             ulLen = (strlenW(lpProps->Value.lpszW) + 1u) * sizeof(WCHAR);
825             lpDest->Value.lpszW = (LPWSTR)lpDataDest;
826             memcpy(lpDest->Value.lpszW, lpProps->Value.lpszW, ulLen);
827             lpDataDest += ulLen;
828             break;
829         case PT_BINARY:
830             lpDest->Value.bin.lpb = (LPBYTE)lpDataDest;
831             memcpy(lpDest->Value.bin.lpb, lpProps->Value.bin.lpb, lpProps->Value.bin.cb);
832             lpDataDest += lpProps->Value.bin.cb;
833             break;
834         default:
835             if (lpProps->ulPropTag & MV_FLAG)
836             {
837                 lpDest->Value.MVi.cValues = lpProps->Value.MVi.cValues;
838                 /* Note: Assignment uses lppszA but covers all cases by union aliasing */
839                 lpDest->Value.MVszA.lppszA = (char**)lpDataDest;
840
841                 switch (PROP_TYPE(lpProps->ulPropTag))
842                 {
843                 case PT_MV_STRING8:
844                 {
845                     lpDataDest += lpProps->Value.MVszA.cValues * sizeof(char *);
846
847                     for (i = 0; i < lpProps->Value.MVszA.cValues; i++)
848                     {
849                         ULONG ulStrLen = lstrlenA(lpProps->Value.MVszA.lppszA[i]) + 1u;
850
851                         lpDest->Value.MVszA.lppszA[i] = lpDataDest;
852                         memcpy(lpDataDest, lpProps->Value.MVszA.lppszA[i], ulStrLen);
853                         lpDataDest += ulStrLen;
854                     }
855                     break;
856                 }
857                 case PT_MV_UNICODE:
858                 {
859                     lpDataDest += lpProps->Value.MVszW.cValues * sizeof(WCHAR *);
860
861                     for (i = 0; i < lpProps->Value.MVszW.cValues; i++)
862                     {
863                         ULONG ulStrLen = (strlenW(lpProps->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR);
864
865                         lpDest->Value.MVszW.lppszW[i] = (LPWSTR)lpDataDest;
866                         memcpy(lpDataDest, lpProps->Value.MVszW.lppszW[i], ulStrLen);
867                         lpDataDest += ulStrLen;
868                     }
869                     break;
870                 }
871                 case PT_MV_BINARY:
872                 {
873                     lpDataDest += lpProps->Value.MVszW.cValues * sizeof(SBinary);
874
875                     for (i = 0; i < lpProps->Value.MVszW.cValues; i++)
876                     {
877                         lpDest->Value.MVbin.lpbin[i].cb = lpProps->Value.MVbin.lpbin[i].cb;
878                         lpDest->Value.MVbin.lpbin[i].lpb = (LPBYTE)lpDataDest;
879                         memcpy(lpDataDest, lpProps->Value.MVbin.lpbin[i].lpb, lpDest->Value.MVbin.lpbin[i].cb);
880                         lpDataDest += lpDest->Value.MVbin.lpbin[i].cb;
881                     }
882                     break;
883                 }
884                 default:
885                     /* No embedded pointers, just copy the data over */
886                     ulLen = UlPropSize(lpProps);
887                     memcpy(lpDest->Value.MVi.lpi, lpProps->Value.MVi.lpi, ulLen);
888                     lpDataDest += ulLen;
889                     break;
890                 }
891                 break;
892             }
893         }
894         lpDest++;
895         lpProps++;
896     }
897     if (lpCount)
898         *lpCount = lpDataDest - (char *)lpDst;
899
900     return S_OK;
901 }
902
903 /*************************************************************************
904  * ScRelocProps@20 (MAPI32.172)
905  *
906  * Relocate the pointers in an array of property values after it has been copied.
907  *
908  * PARAMS
909  *  cValues   [I] Number of properties in lpProps
910  *  lpProps   [O] Property array to relocate the pointers in.
911  *  lpOld     [I] Position where the data was copied from
912  *  lpNew     [I] Position where the data was copied to
913  *  lpCount   [O] If non-NULL, destination for the number of bytes of data at lpDst
914  *
915  * RETURNS
916  *  Success: S_OK. Any pointers in lpProps are relocated.
917  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
918  *
919  * NOTES
920  *  MSDN states that this function can be used for serialisation by passing
921  *  NULL as either lpOld or lpNew, thus converting any pointers in lpProps
922  *  between offsets and pointers. This does not work in native (it crashes),
923  *  and cannot be made to work in Wine because the original interface design
924  *  is deficient. The only use left for this function is to remap pointers
925  *  in a contiguous property array that has been copied with memcpy() to
926  *  another memory location.
927  */
928 SCODE WINAPI ScRelocProps(int cValues, LPSPropValue lpProps, LPVOID lpOld,
929                           LPVOID lpNew, ULONG *lpCount)
930 {
931     static const BOOL bBadPtr = TRUE; /* Windows bug - Assumes source is bad */
932     LPSPropValue lpDest = (LPSPropValue)lpProps;
933     ULONG ulCount = cValues * sizeof(SPropValue);
934     ULONG ulLen, i;
935     int iter;
936
937     TRACE("(%d,%p,%p,%p,%p)\n", cValues, lpProps, lpOld, lpNew, lpCount);
938
939     if (!lpProps || cValues < 0 || !lpOld || !lpNew)
940         return MAPI_E_INVALID_PARAMETER;
941
942     /* The reason native doesn't work as MSDN states is that it assumes that
943      * the lpProps pointer contains valid pointers. This is obviously not
944      * true if the array is being read back from serialisation (the pointers
945      * are just offsets). Native can't actually work converting the pointers to
946      * offsets either, because it converts any array pointers to offsets then
947      * _dereferences the offset_ in order to convert the array elements!
948      *
949      * The code below would handle both cases except that the design of this
950      * function makes it impossible to know when the pointers in lpProps are
951      * valid. If both lpOld and lpNew are non-NULL, native reads the pointers
952      * after converting them, so we must do the same. It seems this
953      * functionality was never tested by MS.
954      */
955
956 #define RELOC_PTR(p) (((char*)(p)) - (char*)lpOld + (char*)lpNew)
957
958     for (iter = 0; iter < cValues; iter++)
959     {
960         switch (PROP_TYPE(lpDest->ulPropTag))
961         {
962         case PT_CLSID:
963             lpDest->Value.lpguid = (LPGUID)RELOC_PTR(lpDest->Value.lpguid);
964             ulCount += sizeof(GUID);
965             break;
966         case PT_STRING8:
967             ulLen = bBadPtr ? 0 : lstrlenA(lpDest->Value.lpszA) + 1u;
968             lpDest->Value.lpszA = (LPSTR)RELOC_PTR(lpDest->Value.lpszA);
969             if (bBadPtr)
970                 ulLen = lstrlenA(lpDest->Value.lpszA) + 1u;
971             ulCount += ulLen;
972             break;
973         case PT_UNICODE:
974             ulLen = bBadPtr ? 0 : (lstrlenW(lpDest->Value.lpszW) + 1u) * sizeof(WCHAR);
975             lpDest->Value.lpszW = (LPWSTR)RELOC_PTR(lpDest->Value.lpszW);
976             if (bBadPtr)
977                 ulLen = (strlenW(lpDest->Value.lpszW) + 1u) * sizeof(WCHAR);
978             ulCount += ulLen;
979             break;
980         case PT_BINARY:
981             lpDest->Value.bin.lpb = (LPBYTE)RELOC_PTR(lpDest->Value.bin.lpb);
982             ulCount += lpDest->Value.bin.cb;
983             break;
984         default:
985             if (lpDest->ulPropTag & MV_FLAG)
986             {
987                 /* Since we have to access the array elements, don't map the
988                  * array unless it is invalid (otherwise, map it at the end)
989                  */
990                 if (bBadPtr)
991                     lpDest->Value.MVszA.lppszA = (LPSTR*)RELOC_PTR(lpDest->Value.MVszA.lppszA);
992
993                 switch (PROP_TYPE(lpProps->ulPropTag))
994                 {
995                 case PT_MV_STRING8:
996                 {
997                     ulCount += lpDest->Value.MVszA.cValues * sizeof(char *);
998
999                     for (i = 0; i < lpDest->Value.MVszA.cValues; i++)
1000                     {
1001                         ULONG ulStrLen = bBadPtr ? 0 : lstrlenA(lpDest->Value.MVszA.lppszA[i]) + 1u;
1002
1003                         lpDest->Value.MVszA.lppszA[i] = (LPSTR)RELOC_PTR(lpDest->Value.MVszA.lppszA[i]);
1004                         if (bBadPtr)
1005                             ulStrLen = lstrlenA(lpDest->Value.MVszA.lppszA[i]) + 1u;
1006                         ulCount += ulStrLen;
1007                     }
1008                     break;
1009                 }
1010                 case PT_MV_UNICODE:
1011                 {
1012                     ulCount += lpDest->Value.MVszW.cValues * sizeof(WCHAR *);
1013
1014                     for (i = 0; i < lpDest->Value.MVszW.cValues; i++)
1015                     {
1016                         ULONG ulStrLen = bBadPtr ? 0 : (strlenW(lpDest->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR);
1017
1018                         lpDest->Value.MVszW.lppszW[i] = (LPWSTR)RELOC_PTR(lpDest->Value.MVszW.lppszW[i]);
1019                         if (bBadPtr)
1020                             ulStrLen = (strlenW(lpDest->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR);
1021                         ulCount += ulStrLen;
1022                     }
1023                     break;
1024                 }
1025                 case PT_MV_BINARY:
1026                 {
1027                     ulCount += lpDest->Value.MVszW.cValues * sizeof(SBinary);
1028
1029                     for (i = 0; i < lpDest->Value.MVszW.cValues; i++)
1030                     {
1031                         lpDest->Value.MVbin.lpbin[i].lpb = (LPBYTE)RELOC_PTR(lpDest->Value.MVbin.lpbin[i].lpb);
1032                         ulCount += lpDest->Value.MVbin.lpbin[i].cb;
1033                     }
1034                     break;
1035                 }
1036                 default:
1037                     ulCount += UlPropSize(lpDest);
1038                     break;
1039                 }
1040                 if (!bBadPtr)
1041                     lpDest->Value.MVszA.lppszA = (LPSTR*)RELOC_PTR(lpDest->Value.MVszA.lppszA);
1042                 break;
1043             }
1044         }
1045         lpDest++;
1046     }
1047     if (lpCount)
1048         *lpCount = ulCount;
1049
1050     return S_OK;
1051 }
1052
1053 /*************************************************************************
1054  * LpValFindProp@12 (MAPI32.173)
1055  *
1056  * Find a property with a given property id in a property array.
1057  *
1058  * PARAMS
1059  *  ulPropTag [I] Property tag containing property id to find
1060  *  cValues   [I] Number of properties in lpProps
1061  *  lpProps   [I] Property array to search
1062  *
1063  * RETURNS
1064  *  A pointer to the matching property, or NULL if none was found.
1065  *
1066  * NOTES
1067  *  This function matches only on the property id and does not care if the
1068  *  property types differ.
1069  */
1070 LPSPropValue WINAPI LpValFindProp(ULONG ulPropTag, ULONG cValues, LPSPropValue lpProps)
1071 {
1072     TRACE("(%ld,%ld,%p)\n", ulPropTag, cValues, lpProps);
1073
1074     if (lpProps && cValues)
1075     {
1076         ULONG i;
1077         for (i = 0; i < cValues; i++)
1078         {
1079             if (PROP_ID(ulPropTag) == PROP_ID(lpProps[i].ulPropTag))
1080                 return &lpProps[i];
1081         }
1082     }
1083     return NULL;
1084 }
1085
1086 /*************************************************************************
1087  * ScDupPropset@16 (MAPI32.174)
1088  *
1089  * Duplicate a property value array into a contiguous block of memory.
1090  *
1091  * PARAMS
1092  *  cValues   [I] Number of properties in lpProps
1093  *  lpProps   [I] Property array to duplicate
1094  *  lpAlloc   [I] Memory allocation function, use MAPIAllocateBuffer()
1095  *  lpNewProp [O] Destination for the newly duplicated property value array
1096  *
1097  * RETURNS
1098  *  Success: S_OK. *lpNewProp contains the duplicated array.
1099  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid,
1100  *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails.
1101  */
1102 SCODE WINAPI ScDupPropset(int cValues, LPSPropValue lpProps,
1103                           LPALLOCATEBUFFER lpAlloc, LPSPropValue *lpNewProp)
1104 {
1105     ULONG ulCount;
1106     SCODE sc;
1107
1108     TRACE("(%d,%p,%p,%p)\n", cValues, lpProps, lpAlloc, lpNewProp);
1109
1110     sc = ScCountProps(cValues, lpProps, &ulCount);
1111     if (SUCCEEDED(sc))
1112     {
1113         sc = lpAlloc(ulCount, (LPVOID*)lpNewProp);
1114         if (SUCCEEDED(sc))
1115             sc = ScCopyProps(cValues, lpProps, *lpNewProp, &ulCount);
1116     }
1117     return sc;
1118 }
1119
1120 /*************************************************************************
1121  * FBadRglpszA@8 (MAPI32.175)
1122  *
1123  * Determine if an array of strings is invalid
1124  *
1125  * PARAMS
1126  *  lppszStrs [I] Array of strings to check
1127  *  ulCount   [I] Number of strings in lppszStrs
1128  *
1129  * RETURNS
1130  *  TRUE, if lppszStrs is invalid, FALSE otherwise.
1131  */
1132 BOOL WINAPI FBadRglpszA(LPSTR *lppszStrs, ULONG ulCount)
1133 {
1134     ULONG i;
1135
1136     TRACE("(%p,%ld)\n", lppszStrs, ulCount);
1137
1138     if (!ulCount)
1139         return FALSE;
1140
1141     if (!lppszStrs || IsBadReadPtr(lppszStrs, ulCount * sizeof(LPWSTR)))
1142         return TRUE;
1143
1144     for (i = 0; i < ulCount; i++)
1145     {
1146         if (!lppszStrs[i] || IsBadStringPtrA(lppszStrs[i], -1))
1147             return TRUE;
1148     }
1149     return FALSE;
1150 }
1151
1152 /*************************************************************************
1153  * FBadRglpszW@8 (MAPI32.176)
1154  *
1155  * See FBadRglpszA.
1156  */
1157 BOOL WINAPI FBadRglpszW(LPWSTR *lppszStrs, ULONG ulCount)
1158 {
1159     ULONG i;
1160
1161     TRACE("(%p,%ld)\n", lppszStrs, ulCount);
1162
1163     if (!ulCount)
1164         return FALSE;
1165
1166     if (!lppszStrs || IsBadReadPtr(lppszStrs, ulCount * sizeof(LPWSTR)))
1167         return TRUE;
1168
1169     for (i = 0; i < ulCount; i++)
1170     {
1171         if (!lppszStrs[i] || IsBadStringPtrW(lppszStrs[i], -1))
1172             return TRUE;
1173     }
1174     return FALSE;
1175 }
1176
1177 /*************************************************************************
1178  * FBadRowSet@4 (MAPI32.177)
1179  *
1180  * Determine if a row is invalid
1181  *
1182  * PARAMS
1183  *  lpRow [I] Row to check
1184  *
1185  * RETURNS
1186  *  TRUE, if lpRow is invalid, FALSE otherwise.
1187  */
1188 BOOL WINAPI FBadRowSet(LPSRowSet lpRowSet)
1189 {
1190     ULONG i;
1191     TRACE("(%p)\n", lpRowSet);
1192
1193     if (!lpRowSet || IsBadReadPtr(lpRowSet, CbSRowSet(lpRowSet)))
1194         return TRUE;
1195
1196     for (i = 0; i < lpRowSet->cRows; i++)
1197     {
1198         if (FBadRow(&lpRowSet->aRow[i]))
1199             return TRUE;
1200     }
1201     return FALSE;
1202 }
1203
1204 /*************************************************************************
1205  * FBadPropTag@4 (MAPI32.179)
1206  *
1207  * Determine if a property tag is invalid
1208  *
1209  * PARAMS
1210  *  ulPropTag [I] Property tag to check
1211  *
1212  * RETURNS
1213  *  TRUE, if ulPropTag is invalid, FALSE otherwise.
1214  */
1215 ULONG WINAPI FBadPropTag(ULONG ulPropTag)
1216 {
1217     TRACE("(0x%08lx)\n", ulPropTag);
1218
1219     switch (ulPropTag & (~MV_FLAG & PROP_TYPE_MASK))
1220     {
1221     case PT_UNSPECIFIED:
1222     case PT_NULL:
1223     case PT_I2:
1224     case PT_LONG:
1225     case PT_R4:
1226     case PT_DOUBLE:
1227     case PT_CURRENCY:
1228     case PT_APPTIME:
1229     case PT_ERROR:
1230     case PT_BOOLEAN:
1231     case PT_OBJECT:
1232     case PT_I8:
1233     case PT_STRING8:
1234     case PT_UNICODE:
1235     case PT_SYSTIME:
1236     case PT_CLSID:
1237     case PT_BINARY:
1238         return FALSE;
1239     }
1240     return TRUE;
1241 }
1242
1243 /*************************************************************************
1244  * FBadRow@4 (MAPI32.180)
1245  *
1246  * Determine if a row is invalid
1247  *
1248  * PARAMS
1249  *  lpRow [I] Row to check
1250  *
1251  * RETURNS
1252  *  TRUE, if lpRow is invalid, FALSE otherwise.
1253  */
1254 ULONG WINAPI FBadRow(LPSRow lpRow)
1255 {
1256     ULONG i;
1257     TRACE("(%p)\n", lpRow);
1258
1259     if (!lpRow || IsBadReadPtr(lpRow, sizeof(SRow)) || !lpRow->lpProps ||
1260         IsBadReadPtr(lpRow->lpProps, lpRow->cValues * sizeof(SPropValue)))
1261         return TRUE;
1262
1263     for (i = 0; i < lpRow->cValues; i++)
1264     {
1265         if (FBadProp(&lpRow->lpProps[i]))
1266             return TRUE;
1267     }
1268     return FALSE;
1269 }
1270
1271 /*************************************************************************
1272  * FBadProp@4 (MAPI32.181)
1273  *
1274  * Determine if a property is invalid
1275  *
1276  * PARAMS
1277  *  lpProp [I] Property to check
1278  *
1279  * RETURNS
1280  *  TRUE, if lpProp is invalid, FALSE otherwise.
1281  */
1282 ULONG WINAPI FBadProp(LPSPropValue lpProp)
1283 {
1284     if (!lpProp || IsBadReadPtr(lpProp, sizeof(SPropValue)) ||
1285         FBadPropTag(lpProp->ulPropTag))
1286         return TRUE;
1287
1288     switch (PROP_TYPE(lpProp->ulPropTag))
1289     {
1290     /* Single value properties containing pointers */
1291     case PT_STRING8:
1292         if (!lpProp->Value.lpszA || IsBadStringPtrA(lpProp->Value.lpszA, -1))
1293             return TRUE;
1294         break;
1295     case PT_UNICODE:
1296         if (!lpProp->Value.lpszW || IsBadStringPtrW(lpProp->Value.lpszW, -1))
1297             return TRUE;
1298         break;
1299     case PT_BINARY:
1300         if (IsBadReadPtr(lpProp->Value.bin.lpb, lpProp->Value.bin.cb))
1301             return TRUE;
1302         break;
1303     case PT_CLSID:
1304         if (IsBadReadPtr(lpProp->Value.lpguid, sizeof(GUID)))
1305             return TRUE;
1306         break;
1307
1308     /* Multiple value properties (arrays) containing no pointers */
1309     case PT_MV_I2:
1310         return PROP_BadArray(lpProp, sizeof(SHORT));
1311     case PT_MV_LONG:
1312         return PROP_BadArray(lpProp, sizeof(LONG));
1313     case PT_MV_LONGLONG:
1314         return PROP_BadArray(lpProp, sizeof(LONG64));
1315     case PT_MV_FLOAT:
1316         return PROP_BadArray(lpProp, sizeof(float));
1317     case PT_MV_SYSTIME:
1318         return PROP_BadArray(lpProp, sizeof(FILETIME));
1319     case PT_MV_APPTIME:
1320     case PT_MV_DOUBLE:
1321         return PROP_BadArray(lpProp, sizeof(double));
1322     case PT_MV_CURRENCY:
1323         return PROP_BadArray(lpProp, sizeof(CY));
1324     case PT_MV_CLSID:
1325         return PROP_BadArray(lpProp, sizeof(GUID));
1326
1327     /* Multiple value properties containing pointers */
1328     case PT_MV_STRING8:
1329         return FBadRglpszA(lpProp->Value.MVszA.lppszA,
1330                            lpProp->Value.MVszA.cValues);
1331     case PT_MV_UNICODE:
1332         return FBadRglpszW(lpProp->Value.MVszW.lppszW,
1333                            lpProp->Value.MVszW.cValues);
1334     case PT_MV_BINARY:
1335         return FBadEntryList((LPENTRYLIST)&lpProp->Value.MVbin);
1336     }
1337     return FALSE;
1338 }
1339
1340 /*************************************************************************
1341  * FBadColumnSet@4 (MAPI32.182)
1342  *
1343  * Determine if an array of property tags is invalid
1344  *
1345  * PARAMS
1346  *  lpCols [I] Property tag array to check
1347  *
1348  * RETURNS
1349  *  TRUE, if lpCols is invalid, FALSE otherwise.
1350  */
1351 ULONG WINAPI FBadColumnSet(LPSPropTagArray lpCols)
1352 {
1353     ULONG ulRet = FALSE, i;
1354
1355     TRACE("(%p)\n", lpCols);
1356
1357     if (!lpCols || IsBadReadPtr(lpCols, CbSPropTagArray(lpCols)))
1358         ulRet = TRUE;
1359     else
1360     {
1361         for (i = 0; i < lpCols->cValues; i++)
1362         {
1363             if ((lpCols->aulPropTag[i] & PROP_TYPE_MASK) == PT_ERROR ||
1364                 FBadPropTag(lpCols->aulPropTag[i]))
1365             {
1366                 ulRet = TRUE;
1367                 break;
1368             }
1369         }
1370     }
1371     TRACE("Returning %s\n", ulRet ? "TRUE" : "FALSE");
1372     return ulRet;
1373 }
1374
1375
1376 /**************************************************************************
1377  *  IMAPIProp {MAPI32}
1378  *
1379  * The default Mapi interface for manipulating object properties.
1380  *
1381  * DESCRIPTION
1382  *  This object provides an interface to an objects properties. It is exposed
1383  *  by several types of Mapi objects in order to simplify the querying and
1384  *  modification of properties.
1385  *
1386  * METHODS
1387  */
1388
1389 /* A single property in a property data collection */
1390 typedef struct
1391 {
1392   struct list  entry;
1393   ULONG        ulAccess; /* The property value access level */
1394   LPSPropValue value;    /* The property value */
1395 } IPropDataItem, *LPIPropDataItem;
1396
1397  /* The main property data collection structure */
1398 typedef struct
1399 {
1400     IPropDataVtbl   *lpVtbl;
1401     LONG             lRef;        /* Reference count */
1402     ALLOCATEBUFFER  *lpAlloc;     /* Memory allocation routine */
1403     ALLOCATEMORE    *lpMore;      /* Linked memory allocation routine */
1404     FREEBUFFER      *lpFree;      /* Memory free routine */
1405     ULONG            ulObjAccess; /* Object access level */
1406     ULONG            ulNumValues; /* Number of items in values list */
1407     struct list      values;      /* List of property values */
1408     CRITICAL_SECTION cs;          /* Lock for thread safety */
1409 } IPropDataImpl;
1410
1411 /* Internal - Get a property value, assumes lock is held */
1412 static IPropDataItem *IMAPIPROP_GetValue(IPropDataImpl *This, ULONG ulPropTag)
1413 {
1414     struct list *cursor;
1415
1416     LIST_FOR_EACH(cursor, &This->values)
1417     {
1418         LPIPropDataItem current = LIST_ENTRY(cursor, IPropDataItem, entry);
1419         /* Note that propery types don't have to match, just Id's */
1420         if (PROP_ID(current->value->ulPropTag) == PROP_ID(ulPropTag))
1421             return current;
1422     }
1423     return NULL;
1424 }
1425
1426 /* Internal - Add a new property value, assumes lock is held */
1427 static IPropDataItem *IMAPIPROP_AddValue(IPropDataImpl *This,
1428                                          LPSPropValue lpProp)
1429 {
1430     LPVOID lpMem;
1431     LPIPropDataItem lpNew;
1432     HRESULT hRet;
1433
1434     hRet = This->lpAlloc(sizeof(IPropDataItem), &lpMem);
1435
1436     if (SUCCEEDED(hRet))
1437     {
1438         lpNew = lpMem;
1439         lpNew->ulAccess = IPROP_READWRITE;
1440
1441         /* Allocate the value seperately so we can update it easily */
1442         lpMem = NULL;
1443         hRet = This->lpAlloc(sizeof(SPropValue), &lpMem);
1444         if (SUCCEEDED(hRet))
1445         {
1446             lpNew->value = lpMem;
1447
1448             hRet = PropCopyMore(lpNew->value, lpProp, This->lpMore, lpMem);
1449             if (SUCCEEDED(hRet))
1450             {
1451                 list_add_tail(&This->values, &lpNew->entry);
1452                 This->ulNumValues++;
1453                 return lpNew;
1454             }
1455             This->lpFree(lpNew->value);
1456         }
1457         This->lpFree(lpNew);
1458     }
1459     return NULL;
1460 }
1461
1462 /* Internal - Lock an IPropData object */
1463 static inline void IMAPIPROP_Lock(IPropDataImpl *This)
1464 {
1465     RtlEnterCriticalSection(&This->cs);
1466 }
1467
1468 /* Internal - Unlock an IPropData object */
1469 static inline void IMAPIPROP_Unlock(IPropDataImpl *This)
1470 {
1471     RtlLeaveCriticalSection(&This->cs);
1472 }
1473
1474 /* This one seems to be missing from mapidefs.h */
1475 #define CbNewSPropProblemArray(c) \
1476     (offsetof(SPropProblemArray,aProblem)+(c)*sizeof(SPropProblem))
1477
1478 /**************************************************************************
1479  *  IMAPIProp_QueryInterface {MAPI32}
1480  *
1481  * Inherited method from the IUnknown Interface.
1482  * See IUnknown_QueryInterface.
1483  *
1484  * NOTES
1485  * This object exposes the following interfaces:
1486  * - IUnknown() : The default interface for all COM-Objects.
1487  * - IMAPIProp() : The default Mapi interface for manipulating object properties.
1488  */
1489 static inline HRESULT WINAPI
1490 IMAPIProp_fnQueryInterface(LPMAPIPROP iface, REFIID riid, LPVOID *ppvObj)
1491 {
1492     IPropDataImpl *This = (IPropDataImpl*)iface;
1493
1494     TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppvObj);
1495
1496     if (!ppvObj || !riid)
1497         return MAPI_E_INVALID_PARAMETER;
1498
1499     *ppvObj = NULL;
1500
1501     if(IsEqualIID(riid, &IID_IUnknown) ||
1502        IsEqualIID(riid, &IID_IMAPIProp) ||
1503        IsEqualIID(riid, &IID_IMAPIPropData))
1504     {
1505         *ppvObj = This;
1506         IPropData_AddRef(iface);
1507         TRACE("returning %p\n", *ppvObj);
1508         return S_OK;
1509     }
1510
1511     TRACE("returning E_NOINTERFACE\n");
1512     return MAPI_E_INTERFACE_NOT_SUPPORTED;
1513 }
1514
1515 /**************************************************************************
1516  *  IMAPIProp_AddRef {MAPI32}
1517  *
1518  * Inherited method from the IUnknown Interface.
1519  * See IUnknown_AddRef.
1520  */
1521 static inline ULONG WINAPI IMAPIProp_fnAddRef(LPMAPIPROP iface)
1522 {
1523     IPropDataImpl *This = (IPropDataImpl*)iface;
1524
1525     TRACE("(%p)->(count before=%lu)\n", This, This->lRef);
1526
1527     return InterlockedIncrement(&This->lRef);
1528 }
1529
1530 /**************************************************************************
1531  *  IMAPIProp_Release {MAPI32}
1532  *
1533  * Inherited method from the IUnknown Interface.
1534  * See IUnknown_Release.
1535  */
1536 static inline ULONG WINAPI IMAPIProp_fnRelease(LPMAPIPROP iface)
1537 {
1538     IPropDataImpl *This = (IPropDataImpl*)iface;
1539     LONG lRef;
1540
1541     TRACE("(%p)->(count before=%lu)\n", This, This->lRef);
1542
1543     lRef = InterlockedDecrement(&This->lRef);
1544     if (!lRef)
1545     {
1546         TRACE("Destroying IPropData (%p)\n",This);
1547
1548         /* Note: No need to lock, since no other thread is referencing iface */
1549         while (!list_empty(&This->values))
1550         {
1551             struct list *head = list_head(&This->values);
1552             LPIPropDataItem current = LIST_ENTRY(head, IPropDataItem, entry);
1553             list_remove(head);
1554             This->lpFree(current->value);
1555             This->lpFree(current);
1556         }
1557         RtlDeleteCriticalSection(&This->cs);
1558         This->lpFree(This);
1559     }
1560     return (ULONG)lRef;
1561 }
1562
1563 /**************************************************************************
1564  *  IMAPIProp_GetLastError {MAPI32}
1565  *
1566  * Get information about the last error that ocurred in an IMAPIProp object.
1567  *
1568  * PARAMS
1569  *  iface    [I] IMAPIProp object that experienced the error
1570  *  hRes     [I] Result of the call that returned an error
1571  *  ulFlags  [I] 0=return Ascii strings, MAPI_UNICODE=return Unicode strings
1572  *  lppError [O] Destination for detailed error information
1573  *
1574  * RETURNS
1575  *  Success: S_OK. *lppError contains details about the last error.
1576  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid,
1577  *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails.
1578  *
1579  * NOTES
1580  *  - If this function succeeds, the returned information in *lppError must be
1581  *  freed using MAPIFreeBuffer() once the caller is finished with it.
1582  *  - It is possible for this function to suceed and set *lppError to NULL,
1583  *  if there is no further information to report about hRes.
1584  */
1585 static inline HRESULT WINAPI
1586 IMAPIProp_fnGetLastError(LPMAPIPROP iface, HRESULT hRes,
1587                          ULONG ulFlags, LPMAPIERROR *lppError)
1588 {
1589     TRACE("(%p,0x%08lX,0x%08lX,%p)\n", iface, hRes, ulFlags, lppError);
1590
1591     if (!lppError  || SUCCEEDED(hRes) || (ulFlags & ~MAPI_UNICODE))
1592         return MAPI_E_INVALID_PARAMETER;
1593
1594     *lppError = NULL;
1595     return S_OK;
1596 }
1597
1598 /**************************************************************************
1599  *  IMAPIProp_SaveChanges {MAPI32}
1600  *
1601  * Update any changes made to a tansactional IMAPIProp object.
1602  *
1603  * PARAMS
1604  *  iface    [I] IMAPIProp object to update
1605  *  ulFlags  [I] Flags controlling the update.
1606  *
1607  * RETURNS
1608  *  Success: S_OK. Any outstanding changes are committed to the object.
1609  *  Failure: An HRESULT error code describing the error.
1610  */
1611 static inline HRESULT WINAPI
1612 IMAPIProp_fnSaveChanges(LPMAPIPROP iface, ULONG ulFlags)
1613 {
1614     TRACE("(%p,0x%08lX)\n", iface, ulFlags);
1615
1616      /* Since this object is not transacted we do not need to implement this */
1617      /* FIXME: Should we set the access levels to clean? */
1618     return S_OK;
1619 }
1620
1621 /**************************************************************************
1622  *  IMAPIProp_GetProps {MAPI32}
1623  *
1624  * Get property values from an IMAPIProp object.
1625  *
1626  * PARAMS
1627  *  iface    [I] IMAPIProp object to get the property values from
1628  *  lpTags   [I] Property tage of property values to be retrieved
1629  *  ulFlags  [I] Return 0=Ascii MAPI_UNICODE=Unicode strings for
1630  *                 unspecified types
1631  *  lpCount  [O] Destination for number of properties returned
1632  *  lppProps [O] Destination for returned property values
1633  *
1634  * RETURNS
1635  *  Success: S_OK. *lppProps and *lpCount are updated.
1636  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
1637  *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails, or
1638  *           MAPI_W_ERRORS_RETURNED if not all properties were retrieved
1639  *           successfully.
1640  * NOTES
1641  *  - If MAPI_W_ERRORS_RETURNED is returned, any properties that could not be
1642  *    retrieved from iface are present in lppProps with their type
1643  *    changed to PT_ERROR and Id unchanged.
1644  */
1645 static inline HRESULT WINAPI
1646 IMAPIProp_fnGetProps(LPMAPIPROP iface, LPSPropTagArray lpTags,
1647                      ULONG ulFlags, ULONG *lpCount, LPSPropValue *lppProps)
1648 {
1649     ULONG i;
1650     HRESULT hRet = S_OK;
1651     IPropDataImpl *This = (IPropDataImpl*)iface;
1652
1653     TRACE("(%p,%p,0x%08lx,%p,%p) stub\n", iface, lpTags, ulFlags,
1654           lpCount, lppProps);
1655
1656     if (!iface || ulFlags & ~MAPI_UNICODE || !lpTags || *lpCount || !lppProps)
1657         return MAPI_E_INVALID_PARAMETER;
1658
1659     FIXME("semi-stub, flags not supported\n");
1660
1661     *lpCount = lpTags->cValues;
1662     *lppProps = NULL;
1663
1664     if (*lpCount)
1665     {
1666         hRet = MAPIAllocateBuffer(*lpCount * sizeof(SPropValue), (LPVOID*)lppProps);
1667         if (FAILED(hRet))
1668             return hRet;
1669
1670         IMAPIPROP_Lock(This);
1671
1672         for (i = 0; i < lpTags->cValues; i++)
1673         {
1674             HRESULT hRetTmp = E_INVALIDARG;
1675             LPIPropDataItem item;
1676
1677             item = IMAPIPROP_GetValue(This, lpTags->aulPropTag[i]);
1678
1679             if (item)
1680                 hRetTmp = PropCopyMore(&(*lppProps)[i], item->value,
1681                                        This->lpMore, *lppProps);
1682             if (FAILED(hRetTmp))
1683             {
1684                 hRet = MAPI_W_ERRORS_RETURNED;
1685                 (*lppProps)[i].ulPropTag =
1686                     CHANGE_PROP_TYPE(lpTags->aulPropTag[i], PT_ERROR);
1687             }
1688         }
1689
1690         IMAPIPROP_Unlock(This);
1691     }
1692     return hRet;
1693 }
1694
1695 /**************************************************************************
1696  *  MAPIProp_GetPropList {MAPI32}
1697  *
1698  * Get the list of property tags for all values in an IMAPIProp object.
1699  *
1700  * PARAMS
1701  *  iface   [I] IMAPIProp object to get the property tag list from
1702  *  ulFlags [I] Return 0=Ascii MAPI_UNICODE=Unicode strings for
1703  *              unspecified types
1704  *  lppTags [O] Destination for the retrieved peoperty tag list
1705  *
1706  * RETURNS
1707  *  Success: S_OK. *lppTags contains the tags for all available properties.
1708  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
1709  *           MAPI_E_BAD_CHARWIDTH, if Ascii or Unicode strings are requested
1710  *           and that type of string is not supported.
1711  */
1712 static inline HRESULT WINAPI
1713 IMAPIProp_fnGetPropList(LPMAPIPROP iface, ULONG ulFlags,
1714                         LPSPropTagArray *lppTags)
1715 {
1716     IPropDataImpl *This = (IPropDataImpl*)iface;
1717     ULONG i;
1718     HRESULT hRet;
1719
1720     TRACE("(%p,0x%08lx,%p) stub\n", iface, ulFlags, lppTags);
1721
1722     if (!iface || ulFlags & ~MAPI_UNICODE || !lppTags)
1723         return MAPI_E_INVALID_PARAMETER;
1724
1725     FIXME("semi-stub, flags not supported\n");
1726
1727     *lppTags = NULL;
1728
1729     IMAPIPROP_Lock(This);
1730
1731     hRet = MAPIAllocateBuffer(CbNewSPropTagArray(This->ulNumValues),
1732                               (LPVOID*)lppTags);
1733     if (SUCCEEDED(hRet))
1734     {
1735         struct list *cursor;
1736
1737         i = 0;
1738         LIST_FOR_EACH(cursor, &This->values)
1739         {
1740             LPIPropDataItem current = LIST_ENTRY(cursor, IPropDataItem, entry);
1741             (*lppTags)->aulPropTag[i] = current->value->ulPropTag;
1742             i++;
1743         }
1744         (*lppTags)->cValues = This->ulNumValues;
1745     }
1746
1747     IMAPIPROP_Unlock(This);
1748     return hRet;
1749 }
1750
1751 /**************************************************************************
1752  *  IMAPIProp_OpenProperty {MAPI32}
1753  *
1754  * Not documented at this time.
1755  *
1756  * RETURNS
1757  *  An HRESULT success/failure code.
1758  */
1759 static inline HRESULT WINAPI
1760 IMAPIProp_fnOpenProperty(LPMAPIPROP iface, ULONG ulPropTag, LPCIID iid,
1761                          ULONG ulOpts, ULONG ulFlags, LPUNKNOWN *lpUnk)
1762 {
1763     FIXME("(%p,%lu,%s,%lu,0x%08lx,%p) stub\n", iface, ulPropTag,
1764           debugstr_guid(iid), ulOpts, ulFlags, lpUnk);
1765     return MAPI_E_NO_SUPPORT;
1766 }
1767
1768
1769 /**************************************************************************
1770  *  IMAPIProp_SetProps {MAPI32}
1771  *
1772  * Add or edit the property values in an IMAPIProp object.
1773  *
1774  * PARAMS
1775  *  iface    [I] IMAPIProp object to get the property tag list from
1776  *  ulValues [I] Number of properties in lpProps
1777  *  lpProps  [I] Property values to set
1778  *  lppProbs [O] Optional destination for any problems that occurred
1779  *
1780  * RETURNS
1781  *  Success: S_OK. The properties in lpProps are added to iface if they don't
1782  *           exist, or changed to the values in lpProps if they do
1783  *  Failure: An HRESULT error code describing the error
1784  */
1785 static inline HRESULT WINAPI
1786 IMAPIProp_fnSetProps(LPMAPIPROP iface, ULONG ulValues,
1787                      LPSPropValue lpProps, LPSPropProblemArray *lppProbs)
1788 {
1789     IPropDataImpl *This = (IPropDataImpl*)iface;
1790     HRESULT hRet = S_OK;
1791     ULONG i;
1792
1793     TRACE("(%p,%lu,%p,%p)\n", iface, ulValues, lpProps, lppProbs);
1794
1795     if (!iface || !lpProps)
1796       return MAPI_E_INVALID_PARAMETER;
1797
1798     for (i = 0; i < ulValues; i++)
1799     {
1800         if (FBadProp(&lpProps[i]) ||
1801             PROP_TYPE(lpProps[i].ulPropTag) == PT_OBJECT ||
1802             PROP_TYPE(lpProps[i].ulPropTag) == PT_NULL)
1803           return MAPI_E_INVALID_PARAMETER;
1804     }
1805
1806     IMAPIPROP_Lock(This);
1807
1808     /* FIXME: Under what circumstances is lpProbs created? */
1809     for (i = 0; i < ulValues; i++)
1810     {
1811         LPIPropDataItem item = IMAPIPROP_GetValue(This, lpProps[i].ulPropTag);
1812
1813         if (item)
1814         {
1815             HRESULT hRetTmp;
1816             LPVOID lpMem = NULL;
1817
1818             /* Found, so update the existing value */
1819             if (item->value->ulPropTag != lpProps[i].ulPropTag)
1820                 FIXME("semi-stub, overwriting type (not coercing)\n");
1821
1822             hRetTmp = This->lpAlloc(sizeof(SPropValue), &lpMem);
1823             if (SUCCEEDED(hRetTmp))
1824             {
1825                 hRetTmp = PropCopyMore(lpMem, &lpProps[i], This->lpMore, lpMem);
1826                 if (SUCCEEDED(hRetTmp))
1827                 {
1828                     This->lpFree(item->value);
1829                     item->value = lpMem;
1830                     continue;
1831                 }
1832                 This->lpFree(lpMem);
1833             }
1834             hRet = hRetTmp;
1835         }
1836         else
1837         {
1838             /* Add new value */
1839             if (!(item = IMAPIPROP_AddValue(This, &lpProps[i])))
1840                 hRet = MAPI_E_NOT_ENOUGH_MEMORY;
1841         }
1842     }
1843
1844     IMAPIPROP_Unlock(This);
1845     return hRet;
1846 }
1847
1848 /**************************************************************************
1849  *  IMAPIProp_DeleteProps {MAPI32}
1850  *
1851  * Delete one or more property values from an IMAPIProp object.
1852  *
1853  * PARAMS
1854  *  iface    [I] IMAPIProp object to remove property values from.
1855  *  lpTags   [I] Collection of property Id's to remove from iface.
1856  *  lppProbs [O] Destination for problems encountered, if any.
1857  *
1858  * RETURNS
1859  *  Success: S_OK. Any properties in iface matching property Id's in lpTags have
1860  *           been deleted. If lppProbs is non-NULL it contains details of any
1861  *           errors that occurred.
1862  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
1863  *           E_ACCESSDENIED, if this object was created using CreateIProp() and
1864  *           a subsequent call to IPropData_SetObjAcess() was made specifying
1865  *           IPROP_READONLY as the access type.
1866  *
1867  * NOTES
1868  *  - lppProbs will not be populated for cases where a property Id is present
1869  *    in lpTags but not in iface.
1870  *  - lppProbs should be deleted with MAPIFreeBuffer() if returned.
1871  */
1872 static inline HRESULT WINAPI
1873 IMAPIProp_fnDeleteProps(LPMAPIPROP iface, LPSPropTagArray lpTags,
1874                         LPSPropProblemArray *lppProbs)
1875 {
1876     IPropDataImpl *This = (IPropDataImpl*)iface;
1877     ULONG i, numProbs = 0;
1878     HRESULT hRet = S_OK;
1879
1880     TRACE("(%p,%p,%p)\n", iface, lpTags, lppProbs);
1881
1882     if (!iface || !lpTags)
1883         return MAPI_E_INVALID_PARAMETER;
1884
1885     if (lppProbs)
1886         *lppProbs = NULL;
1887
1888     for (i = 0; i < lpTags->cValues; i++)
1889     {
1890         if (FBadPropTag(lpTags->aulPropTag[i]) ||
1891             PROP_TYPE(lpTags->aulPropTag[i]) == PT_OBJECT ||
1892             PROP_TYPE(lpTags->aulPropTag[i]) == PT_NULL)
1893           return MAPI_E_INVALID_PARAMETER;
1894     }
1895
1896     IMAPIPROP_Lock(This);
1897
1898     if (This->ulObjAccess != IPROP_READWRITE)
1899     {
1900         IMAPIPROP_Unlock(This);
1901         return E_ACCESSDENIED;
1902     }
1903
1904     for (i = 0; i < lpTags->cValues; i++)
1905     {
1906         LPIPropDataItem item = IMAPIPROP_GetValue(This, lpTags->aulPropTag[i]);
1907
1908         if (item)
1909         {
1910             if (item->ulAccess & IPROP_READWRITE)
1911             {
1912                 /* Everything hunky-dory, remove the item */
1913                 list_remove(&item->entry);
1914                 This->lpFree(item->value); /* Also frees value pointers */
1915                 This->lpFree(item);
1916                 This->ulNumValues--;
1917             }
1918             else if (lppProbs)
1919             {
1920                  /* Can't write the value. Create/populate problems array */
1921                  if (!*lppProbs)
1922                  {
1923                      /* Create problems array */
1924                      ULONG ulSize = CbNewSPropProblemArray(lpTags->cValues - i);
1925                      HRESULT hRetTmp = MAPIAllocateBuffer(ulSize, (LPVOID*)lppProbs);
1926                      if (FAILED(hRetTmp))
1927                          hRet = hRetTmp;
1928                  }
1929                  if (*lppProbs)
1930                  {
1931                      LPSPropProblem lpProb = &(*lppProbs)->aProblem[numProbs];
1932                      lpProb->ulIndex = i;
1933                      lpProb->ulPropTag = lpTags->aulPropTag[i];
1934                      lpProb->scode = E_ACCESSDENIED;
1935                      numProbs++;
1936                  }
1937             }
1938         }
1939     }
1940     if (lppProbs && *lppProbs)
1941         (*lppProbs)->cProblem = numProbs;
1942
1943     IMAPIPROP_Unlock(This);
1944     return hRet;
1945 }
1946
1947
1948 /**************************************************************************
1949  *  IMAPIProp_CopyTo {MAPI32}
1950  *
1951  * Not documented at this time.
1952  *
1953  * RETURNS
1954  *  An HRESULT success/failure code.
1955  */
1956 static inline HRESULT WINAPI
1957 IMAPIProp_fnCopyTo(LPMAPIPROP iface, ULONG niids, LPCIID lpiidExcl,
1958                    LPSPropTagArray lpPropsExcl, ULONG ulParam,
1959                    LPMAPIPROGRESS lpIProgress, LPCIID lpIfaceIid, LPVOID lpDstObj,
1960                    ULONG ulFlags, LPSPropProblemArray *lppProbs)
1961 {
1962     FIXME("(%p,%lu,%p,%p,%lx,%p,%s,%p,0x%08lX,%p) stub\n", iface, niids,
1963           lpiidExcl, lpPropsExcl, ulParam, lpIProgress,
1964           debugstr_guid(lpIfaceIid), lpDstObj, ulFlags, lppProbs);
1965     return MAPI_E_NO_SUPPORT;
1966 }
1967
1968 /**************************************************************************
1969  *  IMAPIProp_CopyProps {MAPI32}
1970  *
1971  * Not documented at this time.
1972  *
1973  * RETURNS
1974  *  An HRESULT success/failure code.
1975  */
1976 static inline HRESULT WINAPI
1977 IMAPIProp_fnCopyProps(LPMAPIPROP iface, LPSPropTagArray lpInclProps,
1978                       ULONG ulParam, LPMAPIPROGRESS lpIProgress, LPCIID lpIface,
1979                       LPVOID lpDstObj, ULONG ulFlags,
1980                       LPSPropProblemArray *lppProbs)
1981 {
1982     FIXME("(%p,%p,%lx,%p,%s,%p,0x%08lX,%p) stub\n", iface, lpInclProps,
1983           ulParam, lpIProgress, debugstr_guid(lpIface), lpDstObj, ulFlags,
1984           lppProbs);
1985     return MAPI_E_NO_SUPPORT;
1986 }
1987
1988 /**************************************************************************
1989  *  IMAPIProp_GetNamesFromIDs {MAPI32}
1990  *
1991  * Get the names of properties from their identifiers.
1992  *
1993  * PARAMS
1994  *  iface       [I]   IMAPIProp object to operate on
1995  *  lppPropTags [I/O] Property identifiers to get the names for, or NULL to
1996  *                    get all names
1997  *  iid         [I]   Property set identifier, or NULL
1998  *  ulFlags     [I]   MAPI_NO_IDS=Don't return numeric named properties,
1999  *                    or MAPI_NO_STRINGS=Don't return strings
2000  *  lpCount     [O]   Destination for number of properties returned
2001  *  lpppNames   [O]   Destination for returned names
2002  *
2003  * RETURNS
2004  *  Success: S_OK. *lppPropTags and lpppNames contain the returned
2005  *           name/identifiers.
2006  *  Failure: MAPI_E_NO_SUPPORT, if the object does not support named properties,
2007  *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails, or
2008  *           MAPI_W_ERRORS_RETURNED if not all properties were retrieved
2009  *           successfully.
2010  */
2011 static inline HRESULT WINAPI
2012 IMAPIProp_fnGetNamesFromIDs(LPMAPIPROP iface, LPSPropTagArray *lppPropTags,
2013                             LPGUID iid, ULONG ulFlags, ULONG *lpCount,
2014                             LPMAPINAMEID **lpppNames)
2015 {
2016     FIXME("(%p,%p,%s,0x%08lX,%p,%p) stub\n", iface, lppPropTags,
2017           debugstr_guid(iid), ulFlags, lpCount, lpppNames);
2018     return MAPI_E_NO_SUPPORT;
2019 }
2020
2021 /**************************************************************************
2022  *  IMAPIProp_GetIDsFromNames {MAPI32}
2023  *
2024  * Get property identifiers associated with one or more named properties.
2025  *
2026  * PARAMS
2027  *  iface       [I] IMAPIProp object to operate on
2028  *  ulNames     [I] Number of names in lppNames
2029  *  lppNames    [I] Names to query or create, or NULL to query all names
2030  *  ulFlags     [I] Pass MAPI_CREATE to create new named properties
2031  *  lppPropTags [O] Destination for queried or created property identifiers
2032  *
2033  * RETURNS
2034  *  Success: S_OK. *lppPropTags contains the property tags created or requested.
2035  *  Failure: MAPI_E_NO_SUPPORT, if the object does not support named properties,
2036  *           MAPI_E_TOO_BIG, if the object cannot process the number of
2037  *           properties involved.
2038  *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails, or
2039  *           MAPI_W_ERRORS_RETURNED if not all properties were retrieved
2040  *           successfully.
2041  */
2042 static inline HRESULT WINAPI
2043 IMAPIProp_fnGetIDsFromNames(LPMAPIPROP iface, ULONG ulNames,
2044                             LPMAPINAMEID *lppNames, ULONG ulFlags,
2045                             LPSPropTagArray *lppPropTags)
2046 {
2047     FIXME("(%p,%ld,%p,0x%08lX,%p) stub\n",
2048           iface, ulNames, lppNames, ulFlags, lppPropTags);
2049     return MAPI_E_NO_SUPPORT;
2050 }
2051
2052 /**************************************************************************
2053  *  IPropData {MAPI32}
2054  *
2055  * A default Mapi interface to provide manipulation of object properties.
2056  *
2057  * DESCRIPTION
2058  *  This object provides a default interface suitable in some cases as an
2059  *  implementation of the IMAPIProp interface (which has no default
2060  *  implementation). In addition to the IMAPIProp() methods inherited, this
2061  *  interface allows read/write control over access to the object and its
2062  *  individual properties.
2063  *
2064  *  To obtain the default implementation of this interface from Mapi, call
2065  *  CreateIProp().
2066  *
2067  * METHODS
2068  */
2069
2070 /**************************************************************************
2071  *  IPropData_QueryInterface {MAPI32}
2072  *
2073  * Inherited method from the IMAPIProp Interface.
2074  * See IMAPIProp_QueryInterface.
2075  */
2076 static HRESULT WINAPI
2077 IPropData_fnQueryInterface(LPPROPDATA iface, REFIID riid, LPVOID *ppvObj)
2078 {
2079     return IMAPIProp_fnQueryInterface((LPMAPIPROP)iface, riid, ppvObj);
2080 }
2081
2082 /**************************************************************************
2083  *  IPropData_AddRef {MAPI32}
2084  *
2085  * Inherited method from the IMAPIProp Interface.
2086  * See IMAPIProp_AddRef.
2087  */
2088 static ULONG WINAPI
2089 IPropData_fnAddRef(LPPROPDATA iface)
2090 {
2091     return IMAPIProp_fnAddRef((LPMAPIPROP)iface);
2092 }
2093
2094 /**************************************************************************
2095  *  IPropData_Release {MAPI32}
2096  *
2097  * Inherited method from the IMAPIProp Interface.
2098  * See IMAPIProp_Release.
2099  */
2100 static ULONG WINAPI
2101 IPropData_fnRelease(LPPROPDATA iface)
2102 {
2103     return IMAPIProp_fnRelease((LPMAPIPROP)iface);
2104 }
2105
2106 /**************************************************************************
2107  *  IPropData_GetLastError {MAPI32}
2108  *
2109  * Inherited method from the IMAPIProp Interface.
2110  * See IMAPIProp_GetLastError.
2111  */
2112 static HRESULT WINAPI
2113 IPropData_fnGetLastError(LPPROPDATA iface, HRESULT hRes, ULONG ulFlags,
2114                          LPMAPIERROR *lppError)
2115 {
2116     return IMAPIProp_fnGetLastError((LPMAPIPROP)iface, hRes, ulFlags, lppError);
2117 }
2118
2119 /**************************************************************************
2120  *  IPropData_SaveChanges {MAPI32}
2121  *
2122  * Inherited method from the IMAPIProp Interface.
2123  * See IMAPIProp_SaveChanges.
2124  */
2125 static HRESULT WINAPI
2126 IPropData_fnSaveChanges(LPPROPDATA iface, ULONG ulFlags)
2127 {
2128     return IMAPIProp_fnSaveChanges((LPMAPIPROP)iface, ulFlags);
2129 }
2130
2131 /**************************************************************************
2132  *  IPropData_GetProps {MAPI32}
2133  *
2134  * Inherited method from the IMAPIProp Interface.
2135  * See IMAPIProp_GetProps.
2136  */
2137 static HRESULT WINAPI
2138 IPropData_fnGetProps(LPPROPDATA iface, LPSPropTagArray lpPropTags,
2139                      ULONG ulFlags, ULONG *lpCount, LPSPropValue *lppProps)
2140 {
2141     return IMAPIProp_fnGetProps((LPMAPIPROP)iface, lpPropTags, ulFlags,
2142                                 lpCount, lppProps);
2143 }
2144
2145 /**************************************************************************
2146  *  IPropData_GetPropList {MAPI32}
2147  *
2148  * Inherited method from the IMAPIProp Interface.
2149  * See IMAPIProp_GetPropList.
2150  */
2151 static HRESULT WINAPI
2152 IPropData_fnGetPropList(LPPROPDATA iface, ULONG ulFlags,
2153                                               LPSPropTagArray *lppPropTags)
2154 {
2155     return IMAPIProp_fnGetPropList((LPMAPIPROP)iface, ulFlags, lppPropTags);
2156 }
2157
2158 /**************************************************************************
2159  *  IPropData_OpenProperty {MAPI32}
2160  *
2161  * Inherited method from the IMAPIProp Interface.
2162  * See IMAPIProp_OpenProperty.
2163  */
2164 static HRESULT WINAPI
2165 IPropData_fnOpenProperty(LPPROPDATA iface, ULONG ulPropTag, LPCIID iid,
2166                          ULONG ulOpts, ULONG ulFlags, LPUNKNOWN *lpUnk)
2167 {
2168     return IMAPIProp_fnOpenProperty((LPMAPIPROP)iface, ulPropTag, iid,
2169                                     ulOpts, ulFlags, lpUnk);
2170 }
2171
2172 /**************************************************************************
2173  *  IPropData_SetProps {MAPI32}
2174  *
2175  * Inherited method from the IMAPIProp Interface.
2176  * See IMAPIProp_SetProps.
2177  */
2178 static HRESULT WINAPI
2179 IPropData_fnSetProps(LPPROPDATA iface, ULONG cValues, LPSPropValue lpProps,
2180                      LPSPropProblemArray *lppProbs)
2181 {
2182     return IMAPIProp_fnSetProps((LPMAPIPROP)iface, cValues, lpProps, lppProbs);
2183 }
2184
2185 /**************************************************************************
2186  *  IPropData_DeleteProps {MAPI32}
2187  *
2188  * Inherited method from the IMAPIProp Interface.
2189  * See IMAPIProp_DeleteProps.
2190  */
2191 static HRESULT WINAPI
2192 IPropData_fnDeleteProps(LPPROPDATA iface, LPSPropTagArray lpPropTags,
2193                         LPSPropProblemArray *lppProbs)
2194 {
2195     return IMAPIProp_fnDeleteProps((LPMAPIPROP)iface, lpPropTags, lppProbs);
2196 }
2197
2198 /**************************************************************************
2199  *  IPropData_CopyTo {MAPI32}
2200  *
2201  * Inherited method from the IMAPIProp Interface.
2202  * See IMAPIProp_CopyTo.
2203  */
2204 static HRESULT WINAPI
2205 IPropData_fnCopyTo(LPPROPDATA iface, ULONG ciidExclude, LPCIID lpIid,
2206                    LPSPropTagArray lpProps, ULONG ulParam,
2207                    LPMAPIPROGRESS lpProgress, LPCIID lpIface, LPVOID lpDst,
2208                    ULONG ulFlags, LPSPropProblemArray *lppProbs)
2209 {
2210     return IMAPIProp_fnCopyTo((LPMAPIPROP)iface, ciidExclude, lpIid, lpProps,
2211                               ulParam, lpProgress, lpIface, lpDst,
2212                               ulFlags, lppProbs);
2213 }
2214
2215 /**************************************************************************
2216  *  IPropData_CopyProps {MAPI32}
2217  *
2218  * Inherited method from the IMAPIProp Interface.
2219  * See IMAPIProp_CopyProps.
2220  */
2221 static HRESULT WINAPI
2222 IPropData_fnCopyProps(LPPROPDATA iface, LPSPropTagArray lpProps,
2223                       ULONG ulParam, LPMAPIPROGRESS lpProgress, LPCIID lpIface,
2224                       LPVOID lpDst, ULONG ulFlags, LPSPropProblemArray *lppProbs)
2225 {
2226     return IMAPIProp_fnCopyProps((LPMAPIPROP)iface, lpProps, ulParam,
2227                                  lpProgress, lpIface, lpDst, ulFlags, lppProbs);
2228 }
2229
2230 /**************************************************************************
2231  *  IPropData_GetNamesFromIDs {MAPI32}
2232  *
2233  * Inherited method from the IMAPIProp Interface.
2234  * See IMAPIProp_GetNamesFromIDs.
2235  */
2236 static HRESULT WINAPI
2237 IPropData_fnGetNamesFromIDs(LPPROPDATA iface, LPSPropTagArray *lppPropTags,
2238                             LPGUID iid, ULONG ulFlags, ULONG *lpCount,
2239                             LPMAPINAMEID **lpppNames)
2240 {
2241     return IMAPIProp_fnGetNamesFromIDs((LPMAPIPROP)iface, lppPropTags, iid,
2242                                        ulFlags, lpCount, lpppNames);
2243 }
2244
2245 /**************************************************************************
2246  *  IPropData_GetIDsFromNames {MAPI32}
2247  *
2248  * Inherited method from the IMAPIProp Interface.
2249  * See IMAPIProp_GetIDsFromNames.
2250  */
2251 static HRESULT WINAPI
2252 IPropData_fnGetIDsFromNames(LPPROPDATA iface, ULONG ulNames,
2253                             LPMAPINAMEID *lppNames, ULONG ulFlags,
2254                             LPSPropTagArray *lppPropTags)
2255 {
2256     return IMAPIProp_fnGetIDsFromNames((LPMAPIPROP)iface, ulNames, lppNames,
2257                                        ulFlags, lppPropTags);
2258 }
2259
2260 /**************************************************************************
2261  *  IPropData_HrSetObjAccess {MAPI32}
2262  *
2263  * Set the access level of an IPropData object.
2264  *
2265  * PARAMS
2266  *  iface    [I] IPropData object to set the access on
2267  *  ulAccess [I] Either IPROP_READONLY or IPROP_READWRITE for read or
2268  *               read/write access respectively.
2269  *
2270  * RETURNS
2271  *  Success: S_OK. The objects access level is changed.
2272  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
2273  */
2274 static HRESULT WINAPI
2275 IPropData_fnHrSetObjAccess(LPPROPDATA iface, ULONG ulAccess)
2276 {
2277     IPropDataImpl *This = (IPropDataImpl*)iface;
2278
2279     TRACE("(%p,%lx)\n", iface, ulAccess);
2280
2281     if (!iface || ulAccess < IPROP_READONLY || ulAccess > IPROP_READWRITE)
2282         return MAPI_E_INVALID_PARAMETER;
2283
2284     IMAPIPROP_Lock(This);
2285
2286     This->ulObjAccess = ulAccess;
2287
2288     IMAPIPROP_Unlock(This);
2289     return S_OK;
2290 }
2291
2292 /* Internal - determine if an access value is bad */
2293 static inline BOOL PROP_IsBadAccess(ULONG ulAccess)
2294 {
2295     switch (ulAccess)
2296     {
2297     case IPROP_READONLY|IPROP_CLEAN:
2298     case IPROP_READONLY|IPROP_DIRTY:
2299     case IPROP_READWRITE|IPROP_CLEAN:
2300     case IPROP_READWRITE|IPROP_DIRTY:
2301         return FALSE;
2302     }
2303     return TRUE;
2304 }
2305
2306 /**************************************************************************
2307  *  IPropData_HrSetPropAccess {MAPI32}
2308  *
2309  * Set the access levels for a group of property values in an IPropData object.
2310  *
2311  * PARAMS
2312  *  iface    [I] IPropData object to set access levels in.
2313  *  lpTags   [I] List of property Id's to set access for.
2314  *  lpAccess [O] Access level for each property in lpTags.
2315  *
2316  * RETURNS
2317  *  Success: S_OK. The access level of each property value in lpTags that is
2318  *           present in iface is changed.
2319  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
2320  *
2321  * NOTES
2322  *  - Each access level in lpAccess must contain at least one of IPROP_READONLY
2323  *    or IPROP_READWRITE, but not both, and also IPROP_CLEAN or IPROP_DIRTY,
2324  *    but not both. No other bits should be set.
2325  *  - If a property Id in lpTags is not present in iface, it is ignored.
2326  */
2327 static HRESULT WINAPI
2328 IPropData_fnHrSetPropAccess(LPPROPDATA iface, LPSPropTagArray lpTags,
2329                             ULONG *lpAccess)
2330 {
2331     IPropDataImpl *This = (IPropDataImpl*)iface;
2332
2333     ULONG i;
2334
2335     TRACE("(%p,%p,%p)\n", iface, lpTags, lpAccess);
2336
2337     if (!iface || !lpTags || !lpAccess)
2338         return MAPI_E_INVALID_PARAMETER;
2339
2340     for (i = 0; i < lpTags->cValues; i++)
2341     {
2342         if (FBadPropTag(lpTags->aulPropTag[i]) || PROP_IsBadAccess(lpAccess[i]))
2343             return MAPI_E_INVALID_PARAMETER;
2344     }
2345
2346     IMAPIPROP_Lock(This);
2347
2348     for (i = 0; i < lpTags->cValues; i++)
2349     {
2350         LPIPropDataItem item = IMAPIPROP_GetValue(This, lpTags->aulPropTag[i]);
2351
2352         if (item)
2353             item->ulAccess = lpAccess[i];
2354     }
2355
2356     IMAPIPROP_Unlock(This);
2357     return S_OK;
2358 }
2359
2360 /**************************************************************************
2361  *  IPropData_HrGetPropAccess {MAPI32}
2362  *
2363  * Get the access levels for a group of property values in an IPropData object.
2364  *
2365  * PARAMS
2366  *  iface     [I] IPropData object to get access levels from.
2367  *  lppTags   [O] Destination for the list of property Id's in iface.
2368  *  lppAccess [O] Destination for access level for each property in lppTags.
2369  *
2370  * RETURNS
2371  *  Success: S_OK. lppTags and lppAccess contain the property Id's and the
2372  *           Access level of each property value in iface.
2373  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid, or
2374  *           MAPI_E_NOT_ENOUGH_MEMORY if memory allocation fails.
2375  *
2376  * NOTES
2377  *  - *lppTags and *lppAccess should be freed with MAPIFreeBuffer() by the caller.
2378  */
2379 static HRESULT WINAPI
2380 IPropData_fnHrGetPropAccess(LPPROPDATA iface, LPSPropTagArray *lppTags,
2381                             ULONG **lppAccess)
2382 {
2383     IPropDataImpl *This = (IPropDataImpl*)iface;
2384     LPVOID lpMem;
2385     HRESULT hRet;
2386     ULONG i;
2387
2388     TRACE("(%p,%p,%p) stub\n", iface, lppTags, lppAccess);
2389
2390     if (!iface || !lppTags || !lppAccess)
2391         return MAPI_E_INVALID_PARAMETER;
2392
2393     *lppTags = NULL;
2394     *lppAccess = NULL;
2395
2396     IMAPIPROP_Lock(This);
2397
2398     hRet = This->lpAlloc(CbNewSPropTagArray(This->ulNumValues), &lpMem);
2399     if (SUCCEEDED(hRet))
2400     {
2401         *lppTags = lpMem;
2402
2403         hRet = This->lpAlloc(This->ulNumValues * sizeof(ULONG), &lpMem);
2404         if (SUCCEEDED(hRet))
2405         {
2406             struct list *cursor;
2407
2408             *lppAccess = lpMem;
2409             (*lppTags)->cValues = This->ulNumValues;
2410
2411             i = 0;
2412             LIST_FOR_EACH(cursor, &This->values)
2413             {
2414                 LPIPropDataItem item = LIST_ENTRY(cursor, IPropDataItem, entry);
2415                 (*lppTags)->aulPropTag[i] = item->value->ulPropTag;
2416                 (*lppAccess)[i] = item->ulAccess;
2417                 i++;
2418             }
2419             IMAPIPROP_Unlock(This);
2420             return S_OK;
2421         }
2422         This->lpFree(*lppTags);
2423         *lppTags = 0;
2424     }
2425     IMAPIPROP_Unlock(This);
2426     return MAPI_E_NOT_ENOUGH_MEMORY;
2427 }
2428
2429 /**************************************************************************
2430  *  IPropData_HrAddObjProps {MAPI32}
2431  *
2432  * Not documented at this time.
2433  *
2434  * RETURNS
2435  *  An HRESULT success/failure code.
2436  */
2437 static HRESULT WINAPI
2438 IPropData_fnHrAddObjProps(LPPROPDATA iface, LPSPropTagArray lpTags,
2439                           LPSPropProblemArray *lppProbs)
2440 {
2441 #if 0
2442     ULONG i;
2443     HRESULT hRet;
2444     LPSPropValue lpValues;
2445 #endif
2446
2447     FIXME("(%p,%p,%p) stub\n", iface, lpTags, lppProbs);
2448
2449     if (!iface || !lpTags)
2450         return MAPI_E_INVALID_PARAMETER;
2451
2452     /* FIXME: Below is the obvious implementation, adding all the properties
2453      *        in lpTags to the object. However, it doesn't appear that this
2454      *        is what this function does.
2455      */
2456     return S_OK;
2457 #if 0
2458     if (!lpTags->cValues)
2459         return S_OK;
2460
2461     lpValues = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2462                          lpTags->cValues * sizeof(SPropValue));
2463     if (!lpValues)
2464         return MAPI_E_NOT_ENOUGH_MEMORY;
2465
2466     for (i = 0; i < lpTags->cValues; i++)
2467         lpValues[i].ulPropTag = lpTags->aulPropTag[i];
2468
2469     hRet = IPropData_SetProps(iface, lpTags->cValues, lpValues, lppProbs);
2470     HeapFree(GetProcessHeap(), 0, lpValues);
2471     return hRet;
2472 #endif
2473 }
2474
2475 static struct IPropDataVtbl IPropDataImpl_vtbl =
2476 {
2477     IPropData_fnQueryInterface,
2478     IPropData_fnAddRef,
2479     IPropData_fnRelease,
2480     IPropData_fnGetLastError,
2481     IPropData_fnSaveChanges,
2482     IPropData_fnGetProps,
2483     IPropData_fnGetPropList,
2484     IPropData_fnOpenProperty,
2485     IPropData_fnSetProps,
2486     IPropData_fnDeleteProps,
2487     IPropData_fnCopyTo,
2488     IPropData_fnCopyProps,
2489     IPropData_fnGetNamesFromIDs,
2490     IPropData_fnGetIDsFromNames,
2491     IPropData_fnHrSetObjAccess,
2492     IPropData_fnHrSetPropAccess,
2493     IPropData_fnHrGetPropAccess,
2494     IPropData_fnHrAddObjProps
2495 };
2496
2497 /*************************************************************************
2498  * CreateIProp@24 (MAPI32.60)
2499  *
2500  * Create an IPropData object.
2501  *
2502  * PARAMS
2503  *  iid         [I] GUID of the object to create. Use &IID_IMAPIPropData or NULL
2504  *  lpAlloc     [I] Memory allocation function. Use MAPIAllocateBuffer()
2505  *  lpMore      [I] Linked memory allocation function. Use MAPIAllocateMore()
2506  *  lpFree      [I] Memory free function. Use MAPIFreeBuffer()
2507  *  lpReserved  [I] Reserved, set to NULL
2508  *  lppPropData [O] Destination for created IPropData object
2509  *
2510  * RETURNS
2511  *  Success: S_OK. *lppPropData contains the newly created object.
2512  *  Failure: MAPI_E_INTERFACE_NOT_SUPPORTED, if iid is non-NULL and not supported,
2513  *           MAPI_E_INVALID_PARAMETER, if any parameter is invalid
2514  */
2515 SCODE WINAPI CreateIProp(LPCIID iid, ALLOCATEBUFFER *lpAlloc,
2516                          ALLOCATEMORE *lpMore, FREEBUFFER *lpFree,
2517                          LPVOID lpReserved, LPPROPDATA *lppPropData)
2518 {
2519     IPropDataImpl *lpPropData;
2520     SCODE scode;
2521
2522     TRACE("(%s,%p,%p,%p,%p,%p)\n", debugstr_guid(iid), lpAlloc, lpMore, lpFree,
2523           lpReserved, lppPropData);
2524
2525     if (lppPropData)
2526         *lppPropData = NULL;
2527
2528     if (iid && !IsEqualGUID(iid, &IID_IMAPIPropData))
2529         return MAPI_E_INTERFACE_NOT_SUPPORTED;
2530
2531     if (!lpAlloc || !lpMore || !lpFree || lpReserved || !lppPropData)
2532         return MAPI_E_INVALID_PARAMETER;
2533
2534     scode = lpAlloc(sizeof(IPropDataImpl), (LPVOID*)&lpPropData);
2535
2536     if (SUCCEEDED(scode))
2537     {
2538         lpPropData->lpVtbl = &IPropDataImpl_vtbl;
2539         lpPropData->lRef = 1;
2540         lpPropData->lpAlloc = lpAlloc;
2541         lpPropData->lpMore = lpMore;
2542         lpPropData->lpFree = lpFree;
2543         lpPropData->ulObjAccess = IPROP_READWRITE;
2544         lpPropData->ulNumValues = 0;
2545         list_init(&lpPropData->values);
2546         RtlInitializeCriticalSection(&lpPropData->cs);
2547         *lppPropData = (LPPROPDATA)lpPropData;
2548     }
2549     return scode;
2550 }