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