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