Added support for DIRID_USERPROFILE.
[wine] / dlls / mapi32 / prop.c
1 /*
2  * Property functions
3  *
4  * Copyright 2004 Jon Griffiths
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdarg.h>
22 #define NONAMELESSUNION
23 #define NONAMELESSSTRUCT
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winreg.h"
27 #include "winerror.h"
28 #include "winternl.h"
29 #include "objbase.h"
30 #include "shlwapi.h"
31 #include "wine/debug.h"
32 #include "wine/unicode.h"
33 #include "mapival.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(mapi);
36
37 BOOL WINAPI FBadRglpszA(LPSTR*,ULONG);
38 BOOL WINAPI FBadRglpszW(LPWSTR*,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             memcpy(lpDest->Value.lpguid, lpSrc->Value.lpguid, sizeof(GUID));
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 seperately, 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         break;
271     case PT_OBJECT:
272     default:             ulRet = 0u;
273                          break;
274     }
275
276     return ulRet;
277 }
278
279 /*************************************************************************
280  * FPropContainsProp@12 (MAPI32.78)
281  *
282  * Find a property with a given property tag in a property array.
283  *
284  * PARAMS
285  *  lpHaystack [I] Property to match to
286  *  lpNeedle   [I] Property to find in lpHaystack
287  *  ulFuzzy    [I] Flags controlling match type and strictness (FL_* flags from "mapidefs.h")
288  *
289  * RETURNS
290  *  TRUE, if lpNeedle matches lpHaystack according to the criteria of ulFuzzy.
291  *
292  * NOTES
293  *  Only property types of PT_STRING8 and PT_BINARY are handled by this function.
294  */
295 BOOL WINAPI FPropContainsProp(LPSPropValue lpHaystack, LPSPropValue lpNeedle, ULONG ulFuzzy)
296 {
297     TRACE("(%p,%p,0x%08lx)\n", lpHaystack, lpNeedle, ulFuzzy);
298
299     if (FBadProp(lpHaystack) || FBadProp(lpNeedle) ||
300         PROP_TYPE(lpHaystack->ulPropTag) != PROP_TYPE(lpNeedle->ulPropTag))
301         return FALSE;
302
303     /* FIXME: Do later versions support Unicode as well? */
304
305     if (PROP_TYPE(lpHaystack->ulPropTag) == PT_STRING8)
306     {
307         DWORD dwFlags = 0, dwNeedleLen, dwHaystackLen;
308
309         if (ulFuzzy & FL_IGNORECASE)
310             dwFlags |= NORM_IGNORECASE;
311         if (ulFuzzy & FL_IGNORENONSPACE)
312             dwFlags |= NORM_IGNORENONSPACE;
313         if (ulFuzzy & FL_LOOSE)
314             dwFlags |= (NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS);
315
316         dwNeedleLen = lstrlenA(lpNeedle->Value.lpszA);
317         dwHaystackLen = lstrlenA(lpHaystack->Value.lpszA);
318
319         if ((ulFuzzy & (FL_SUBSTRING|FL_PREFIX)) == FL_PREFIX)
320         {
321             if (dwNeedleLen <= dwHaystackLen &&
322                 CompareStringA(LOCALE_USER_DEFAULT, dwFlags,
323                                lpHaystack->Value.lpszA, dwNeedleLen,
324                                lpNeedle->Value.lpszA, dwNeedleLen) == CSTR_EQUAL)
325                 return TRUE; /* needle is a prefix of haystack */
326         }
327         else if ((ulFuzzy & (FL_SUBSTRING|FL_PREFIX)) == FL_SUBSTRING)
328         {
329             LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD) = StrChrA;
330             LPSTR lpStr = lpHaystack->Value.lpszA;
331
332             if (dwFlags & NORM_IGNORECASE)
333                 pStrChrFn = StrChrIA;
334
335             while ((lpStr = pStrChrFn(lpStr, *lpNeedle->Value.lpszA)) != NULL)
336             {
337                 dwHaystackLen -= (lpStr - lpHaystack->Value.lpszA);
338                 if (dwNeedleLen <= dwHaystackLen &&
339                     CompareStringA(LOCALE_USER_DEFAULT, dwFlags,
340                                lpStr, dwNeedleLen,
341                                lpNeedle->Value.lpszA, dwNeedleLen) == CSTR_EQUAL)
342                     return TRUE; /* needle is a substring of haystack */
343                 lpStr++;
344             }
345         }
346         else if (CompareStringA(LOCALE_USER_DEFAULT, dwFlags,
347                                 lpHaystack->Value.lpszA, dwHaystackLen,
348                                 lpNeedle->Value.lpszA, dwNeedleLen) == CSTR_EQUAL)
349             return TRUE; /* full string match */
350     }
351     else if (PROP_TYPE(lpHaystack->ulPropTag) == PT_BINARY)
352     {
353         if ((ulFuzzy & (FL_SUBSTRING|FL_PREFIX)) == FL_PREFIX)
354         {
355             if (lpNeedle->Value.bin.cb <= lpHaystack->Value.bin.cb &&
356                 !memcmp(lpNeedle->Value.bin.lpb, lpHaystack->Value.bin.lpb,
357                         lpNeedle->Value.bin.cb))
358                 return TRUE; /* needle is a prefix of haystack */
359         }
360         else if ((ulFuzzy & (FL_SUBSTRING|FL_PREFIX)) == FL_SUBSTRING)
361         {
362             ULONG ulLen = lpHaystack->Value.bin.cb;
363             LPBYTE lpb = lpHaystack->Value.bin.lpb;
364
365             while ((lpb = memchr(lpb, *lpNeedle->Value.bin.lpb, ulLen)) != NULL)
366             {
367                 ulLen = lpHaystack->Value.bin.cb - (lpb - lpHaystack->Value.bin.lpb);
368                 if (lpNeedle->Value.bin.cb <= ulLen &&
369                     !memcmp(lpNeedle->Value.bin.lpb, lpb, lpNeedle->Value.bin.cb))
370                     return TRUE; /* needle is a substring of haystack */
371                 lpb++;
372             }
373         }
374         else if (!LPropCompareProp(lpHaystack, lpNeedle))
375             return TRUE; /* needle is an exact match with haystack */
376
377     }
378     return FALSE;
379 }
380
381 /*************************************************************************
382  * FPropCompareProp@12 (MAPI32.79)
383  *
384  * Compare two properties.
385  *
386  * PARAMS
387  *  lpPropLeft  [I] Left hand property to compare to lpPropRight
388  *  ulOp        [I] Comparison operator (RELOP_* enum from "mapidefs.h")
389  *  lpPropRight [I] Right hand property to compare to lpPropLeft
390  *
391  * RETURNS
392  *  TRUE, if the comparison is true, FALSE otherwise.
393  */
394 BOOL WINAPI FPropCompareProp(LPSPropValue lpPropLeft, ULONG ulOp, LPSPropValue lpPropRight)
395 {
396     LONG iCmp;
397
398     TRACE("(%p,%ld,%p)\n", lpPropLeft, ulOp, lpPropRight);
399
400     if (ulOp > RELOP_RE || FBadProp(lpPropLeft) || FBadProp(lpPropRight))
401         return FALSE;
402
403     if (ulOp == RELOP_RE)
404     {
405         FIXME("Comparison operator RELOP_RE not yet implemented!\n");
406         return FALSE;
407     }
408
409     iCmp = LPropCompareProp(lpPropLeft, lpPropRight);
410
411     switch (ulOp)
412     {
413     case RELOP_LT: return iCmp <  0 ? TRUE : FALSE;
414     case RELOP_LE: return iCmp <= 0 ? TRUE : FALSE;
415     case RELOP_GT: return iCmp >  0 ? TRUE : FALSE;
416     case RELOP_GE: return iCmp >= 0 ? TRUE : FALSE;
417     case RELOP_EQ: return iCmp == 0 ? TRUE : FALSE;
418     case RELOP_NE: return iCmp != 0 ? TRUE : FALSE;
419     }
420     return FALSE;
421 }
422
423 /*************************************************************************
424  * LPropCompareProp@8 (MAPI32.80)
425  *
426  * Compare two properties.
427  *
428  * PARAMS
429  *  lpPropLeft  [I] Left hand property to compare to lpPropRight
430  *  lpPropRight [I] Right hand property to compare to lpPropLeft
431  *
432  * RETURNS
433  *  An integer less than, equal to or greater than 0, indicating that
434  *  lpszStr is less than, the same, or greater than lpszComp.
435  */
436 LONG WINAPI LPropCompareProp(LPSPropValue lpPropLeft, LPSPropValue lpPropRight)
437 {
438     LONG iRet;
439
440     TRACE("(%p->0x%08lx,%p->0x%08lx)\n", lpPropLeft, lpPropLeft->ulPropTag,
441           lpPropRight, lpPropRight->ulPropTag);
442
443     /* If the properties are not the same, sort by property type */
444     if (PROP_TYPE(lpPropLeft->ulPropTag) != PROP_TYPE(lpPropRight->ulPropTag))
445         return (LONG)PROP_TYPE(lpPropLeft->ulPropTag) - (LONG)PROP_TYPE(lpPropRight->ulPropTag);
446
447     switch (PROP_TYPE(lpPropLeft->ulPropTag))
448     {
449     case PT_UNSPECIFIED:
450     case PT_NULL:
451         return 0; /* NULLs are equal */
452     case PT_I2:
453         return lpPropLeft->Value.i - lpPropRight->Value.i;
454     case PT_I4:
455         return lpPropLeft->Value.l - lpPropRight->Value.l;
456     case PT_I8:
457         if (lpPropLeft->Value.li.QuadPart > lpPropRight->Value.li.QuadPart)
458             return 1;
459         if (lpPropLeft->Value.li.QuadPart == lpPropRight->Value.li.QuadPart)
460             return 0;
461         return -1;
462     case PT_R4:
463         if (lpPropLeft->Value.flt > lpPropRight->Value.flt)
464             return 1;
465         if (lpPropLeft->Value.flt == lpPropRight->Value.flt)
466             return 0;
467         return -1;
468     case PT_APPTIME:
469     case PT_R8:
470         if (lpPropLeft->Value.dbl > lpPropRight->Value.dbl)
471             return 1;
472         if (lpPropLeft->Value.dbl == lpPropRight->Value.dbl)
473             return 0;
474         return -1;
475     case PT_CURRENCY:
476         if (lpPropLeft->Value.cur.int64 > lpPropRight->Value.cur.int64)
477             return 1;
478         if (lpPropLeft->Value.cur.int64 == lpPropRight->Value.cur.int64)
479             return 0;
480         return -1;
481     case PT_SYSTIME:
482         return CompareFileTime(&lpPropLeft->Value.ft, &lpPropRight->Value.ft);
483     case PT_BOOLEAN:
484         return (lpPropLeft->Value.b ? 1 : 0) - (lpPropRight->Value.b ? 1 : 0);
485     case PT_BINARY:
486         if (lpPropLeft->Value.bin.cb == lpPropRight->Value.bin.cb)
487             iRet = memcmp(lpPropLeft->Value.bin.lpb, lpPropRight->Value.bin.lpb,
488                           lpPropLeft->Value.bin.cb);
489         else
490         {
491             iRet = memcmp(lpPropLeft->Value.bin.lpb, lpPropRight->Value.bin.lpb,
492                           min(lpPropLeft->Value.bin.cb, lpPropRight->Value.bin.cb));
493
494             if (!iRet)
495                 iRet = lpPropLeft->Value.bin.cb - lpPropRight->Value.bin.cb;
496         }
497         return iRet;
498     case PT_STRING8:
499         return lstrcmpA(lpPropLeft->Value.lpszA, lpPropRight->Value.lpszA);
500     case PT_UNICODE:
501         return strcmpW(lpPropLeft->Value.lpszW, lpPropRight->Value.lpszW);
502     case PT_ERROR:
503         if (lpPropLeft->Value.err > lpPropRight->Value.err)
504             return 1;
505         if (lpPropLeft->Value.err == lpPropRight->Value.err)
506             return 0;
507         return -1;
508     case PT_CLSID:
509         return memcmp(lpPropLeft->Value.lpguid, lpPropRight->Value.lpguid,
510                       sizeof(GUID));
511     }
512     FIXME("Unhandled property type %ld", PROP_TYPE(lpPropLeft->ulPropTag));
513     return 0;
514 }
515
516 /*************************************************************************
517  * PpropFindProp@12 (MAPI32.138)
518  *
519  * Find a property with a given property tag in a property array.
520  *
521  * PARAMS
522  *  lpProps   [I] Property array to search
523  *  cValues   [I] Number of properties in lpProps
524  *  ulPropTag [I] Property tag to find
525  *
526  * RETURNS
527  *  A pointer to the matching property, or NULL if none was found.
528  *
529  * NOTES
530  *  if ulPropTag has a property type of PT_UNSPECIFIED, then only the property
531  *  Ids need to match for a successful match to occur.
532  */
533 LPSPropValue WINAPI PpropFindProp(LPSPropValue lpProps, ULONG cValues, ULONG ulPropTag)
534 {
535     TRACE("(%p,%ld,%ld)\n", lpProps, cValues, ulPropTag);
536
537     if (lpProps && cValues)
538     {
539         ULONG i;
540         for (i = 0; i < cValues; i++)
541         {
542             if (!FBadPropTag(lpProps[i].ulPropTag) &&
543                 (lpProps[i].ulPropTag == ulPropTag ||
544                  (PROP_TYPE(ulPropTag) == PT_UNSPECIFIED &&
545                   PROP_ID(lpProps[i].ulPropTag) == PROP_ID(ulPropTag))))
546                 return &lpProps[i];
547         }
548     }
549     return NULL;
550 }
551
552 /*************************************************************************
553  * ScCountProps@12 (MAPI32.170)
554  *
555  * Validate and determine the length of an array of properties.
556  *
557  * PARAMS
558  *  iCount  [I] Length of the lpProps array
559  *  lpProps [I] Array of properties to validate/size
560  *  pcBytes [O] If non-NULL, destination for the size of the property array
561  *
562  * RETURNS
563  *  Success: S_OK. If pcBytes is non-NULL, it contains the size of the propery array.
564  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid or validation
565  *           of the property array fails.
566  */
567 SCODE WINAPI ScCountProps(INT iCount, LPSPropValue lpProps, ULONG *pcBytes)
568 {
569     ULONG i, ulCount = iCount, ulBytes = 0;
570
571     TRACE("(%d,%p,%p)\n", iCount, lpProps, pcBytes);
572
573     if (iCount <= 0 || !lpProps ||
574         IsBadReadPtr(lpProps, iCount * sizeof(SPropValue)))
575         return MAPI_E_INVALID_PARAMETER;
576
577     for (i = 0; i < ulCount; i++)
578     {
579         ULONG ulPropSize = 0;
580         
581         if (FBadProp(&lpProps[i]) || lpProps[i].ulPropTag == PROP_ID_NULL ||
582             lpProps[i].ulPropTag == PROP_ID_INVALID)
583             return MAPI_E_INVALID_PARAMETER;
584
585             if (PROP_TYPE(lpProps[i].ulPropTag) != PT_OBJECT)
586             {
587                 ulPropSize = UlPropSize(&lpProps[i]);
588                 if (!ulPropSize)
589                     return MAPI_E_INVALID_PARAMETER;
590             }
591             
592             switch (PROP_TYPE(lpProps[i].ulPropTag))
593             {
594             case PT_STRING8:
595             case PT_UNICODE:
596             case PT_CLSID:
597             case PT_BINARY:
598             case PT_MV_I2:       
599             case PT_MV_I4:       
600             case PT_MV_I8:       
601             case PT_MV_R4:       
602             case PT_MV_R8:       
603             case PT_MV_CURRENCY: 
604             case PT_MV_SYSTIME:  
605             case PT_MV_APPTIME:
606                 ulPropSize += sizeof(SPropValue);
607                 break;
608             case PT_MV_CLSID:
609                 ulPropSize += lpProps[i].Value.MVszA.cValues * sizeof(char*) + sizeof(SPropValue);
610                 break;
611             case PT_MV_STRING8:
612             case PT_MV_UNICODE:
613                 ulPropSize += lpProps[i].Value.MVszA.cValues * sizeof(char*) + sizeof(SPropValue);
614                 break;
615             case PT_MV_BINARY:
616                 ulPropSize += lpProps[i].Value.MVbin.cValues * sizeof(SBinary) + sizeof(SPropValue);
617                 break;
618             default:
619                 ulPropSize = sizeof(SPropValue);
620                 break;
621             }
622             ulBytes += ulPropSize;
623     }
624     if (pcBytes)
625         *pcBytes = ulBytes;
626     
627     return S_OK;
628 }
629
630 /*************************************************************************
631  * ScCopyProps@16 (MAPI32.171)
632  *
633  * Copy an array of property values into a buffer suited for serialisation.
634  *
635  * PARAMS
636  *  cValues   [I] Number of properties in lpProps
637  *  lpProps   [I] Property array to copy
638  *  lpDst     [O] Destination for the serialised data
639  *  lpCount   [O] If non-NULL, destination for the number of bytes of data written to lpDst
640  *
641  * RETURNS
642  *  Success: S_OK. lpDst contains the serialised data from lpProps.
643  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
644  *
645  * NOTES
646  *  The resulting property value array is stored in a contigous block starting at lpDst.
647  */
648 SCODE WINAPI ScCopyProps(int cValues, LPSPropValue lpProps, LPVOID lpDst, ULONG *lpCount)
649 {
650     LPSPropValue lpDest = (LPSPropValue)lpDst;
651     char *lpDataDest = (char *)(lpDest + cValues);
652     ULONG ulLen, i;
653     
654     TRACE("(%d,%p,%p,%p)\n", cValues, lpProps, lpDst, lpCount);
655     
656     if (!lpProps || cValues < 0 || !lpDest)
657         return MAPI_E_INVALID_PARAMETER;
658
659     memcpy(lpDst, lpProps, cValues * sizeof(SPropValue));
660     
661     for (i = 0; i < cValues; i++)
662     {
663         switch (PROP_TYPE(lpProps->ulPropTag))
664         {
665         case PT_CLSID:
666             lpDest->Value.lpguid = (LPGUID)lpDataDest;
667             memcpy(lpDest->Value.lpguid, lpProps->Value.lpguid, sizeof(GUID));
668             lpDataDest += sizeof(GUID);
669             break;
670         case PT_STRING8:
671             ulLen = lstrlenA(lpProps->Value.lpszA) + 1u;
672             lpDest->Value.lpszA = lpDataDest;
673             memcpy(lpDest->Value.lpszA, lpProps->Value.lpszA, ulLen);
674             lpDataDest += ulLen;
675             break;
676         case PT_UNICODE:
677             ulLen = (strlenW(lpProps->Value.lpszW) + 1u) * sizeof(WCHAR);
678             lpDest->Value.lpszW = (LPWSTR)lpDataDest;
679             memcpy(lpDest->Value.lpszW, lpProps->Value.lpszW, ulLen);
680             lpDataDest += ulLen;
681             break;
682         case PT_BINARY:
683             lpDest->Value.bin.lpb = (LPBYTE)lpDataDest;
684             memcpy(lpDest->Value.bin.lpb, lpProps->Value.bin.lpb, lpProps->Value.bin.cb);
685             lpDataDest += lpProps->Value.bin.cb;
686             break;
687         default:
688             if (lpProps->ulPropTag & MV_FLAG)
689             {
690                 lpDest->Value.MVi.cValues = lpProps->Value.MVi.cValues;
691                 /* Note: Assignment uses lppszA but covers all cases by union aliasing */
692                 lpDest->Value.MVszA.lppszA = (char**)lpDataDest;
693
694                 switch (PROP_TYPE(lpProps->ulPropTag))
695                 {
696                 case PT_MV_STRING8:
697                 {
698                     lpDataDest += lpProps->Value.MVszA.cValues * sizeof(char *);
699                 
700                     for (i = 0; i < lpProps->Value.MVszA.cValues; i++)
701                     {
702                         ULONG ulStrLen = lstrlenA(lpProps->Value.MVszA.lppszA[i]) + 1u;
703                     
704                         lpDest->Value.MVszA.lppszA[i] = lpDataDest;
705                         memcpy(lpDataDest, lpProps->Value.MVszA.lppszA[i], ulStrLen);
706                         lpDataDest += ulStrLen;
707                     }
708                     break;
709                 }
710                 case PT_MV_UNICODE:
711                 {
712                     lpDataDest += lpProps->Value.MVszW.cValues * sizeof(WCHAR *);
713                 
714                     for (i = 0; i < lpProps->Value.MVszW.cValues; i++)
715                     {
716                         ULONG ulStrLen = (strlenW(lpProps->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR);
717                     
718                         lpDest->Value.MVszW.lppszW[i] = (LPWSTR)lpDataDest;
719                         memcpy(lpDataDest, lpProps->Value.MVszW.lppszW[i], ulStrLen);
720                         lpDataDest += ulStrLen;
721                     }                    
722                     break;
723                 }
724                 case PT_MV_BINARY:
725                 {
726                     lpDataDest += lpProps->Value.MVszW.cValues * sizeof(SBinary);
727                 
728                     for (i = 0; i < lpProps->Value.MVszW.cValues; i++)
729                     {
730                         lpDest->Value.MVbin.lpbin[i].cb = lpProps->Value.MVbin.lpbin[i].cb;
731                         lpDest->Value.MVbin.lpbin[i].lpb = (LPBYTE)lpDataDest;
732                         memcpy(lpDataDest, lpProps->Value.MVbin.lpbin[i].lpb, lpDest->Value.MVbin.lpbin[i].cb);
733                         lpDataDest += lpDest->Value.MVbin.lpbin[i].cb;
734                     }                    
735                     break;
736                 }
737                 default:
738                     /* No embedded pointers, just copy the data over */
739                     ulLen = UlPropSize(lpProps);
740                     memcpy(lpDest->Value.MVi.lpi, lpProps->Value.MVi.lpi, ulLen);
741                     lpDataDest += ulLen;
742                     break;
743                 }
744                 break;
745             }
746         }
747         lpDest++;
748         lpProps++;
749     }
750     if (lpCount)
751         *lpCount = lpDataDest - (char *)lpDst;
752         
753     return S_OK;
754 }
755  
756 /*************************************************************************
757  * ScRelocProps@20 (MAPI32.172)
758  *
759  * Relocate the pointers in an array of property values after it has been copied.
760  *
761  * PARAMS
762  *  cValues   [I] Number of properties in lpProps
763  *  lpProps   [O] Property array to relocate the pointers in.
764  *  lpOld     [I] Position where the data was copied from
765  *  lpNew     [I] Position where the data was copied to
766  *  lpCount   [O] If non-NULL, destination for the number of bytes of data at lpDst
767  *
768  * RETURNS
769  *  Success: S_OK. Any pointers in lpProps are relocated.
770  *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
771  *
772  * NOTES
773  *  MSDN states that this function can be used for serialisation by passing
774  *  NULL as either lpOld or lpNew, thus converting any pointers in lpProps
775  *  between offsets and pointers. This does not work in native (it crashes),
776  *  and cannot be made to work in Wine because the original interface design 
777  *  is deficient. The only use left for this function is to remap pointers
778  *  in a contigous property array that has been copied with memcpy() to another
779  *  memory location.
780  */
781 SCODE WINAPI ScRelocProps(int cValues, LPSPropValue lpProps, LPVOID lpOld, 
782                           LPVOID lpNew, ULONG *lpCount)
783 {
784     static const BOOL bBadPtr = TRUE; /* Windows bug - Assumes source is bad */
785     LPSPropValue lpDest = (LPSPropValue)lpProps;
786     ULONG ulCount = cValues * sizeof(SPropValue);    
787     ULONG ulLen, i;
788     
789     TRACE("(%d,%p,%p,%p,%p)\n", cValues, lpProps, lpOld, lpNew, lpCount);
790     
791     if (!lpProps || cValues < 0 || !lpOld || !lpNew)
792         return MAPI_E_INVALID_PARAMETER;
793
794     /* The reason native doesn't work as MSDN states is that it assumes that
795      * the lpProps pointer contains valid pointers. This is obviously not
796      * true if the array is being read back from serialisation (the pointers
797      * are just offsets). Native can't actually work converting the pointers to
798      * offsets either, because it converts any array pointers to offsets then
799      * _dereferences the offset_ in order to convert the array elements!
800      *
801      * The code below would handle both cases except that the design of this
802      * function makes it impossible to know when the pointers in lpProps are
803      * valid. If both lpOld and lpNew are non-NULL, native reads the pointers
804      * after converting them, so we must do the same. Its seems this 
805      * functionality was never tested by MS.
806      */
807  
808 #define RELOC_PTR(p) (((char*)(p)) - (char*)lpOld + (char*)lpNew)
809         
810     for (i = 0; i < cValues; i++)
811     {
812         switch (PROP_TYPE(lpDest->ulPropTag))
813         {
814         case PT_CLSID:
815             lpDest->Value.lpguid = (LPGUID)RELOC_PTR(lpDest->Value.lpguid);
816             ulCount += sizeof(GUID);
817             break;
818         case PT_STRING8:
819             ulLen = bBadPtr ? 0 : lstrlenA(lpDest->Value.lpszA) + 1u;
820             lpDest->Value.lpszA = (LPSTR)RELOC_PTR(lpDest->Value.lpszA);
821             if (bBadPtr)
822                 ulLen = lstrlenA(lpDest->Value.lpszA) + 1u;
823             ulCount += ulLen;
824             break;
825         case PT_UNICODE:
826             ulLen = bBadPtr ? 0 : (lstrlenW(lpDest->Value.lpszW) + 1u) * sizeof(WCHAR);
827             lpDest->Value.lpszW = (LPWSTR)RELOC_PTR(lpDest->Value.lpszW);
828             if (bBadPtr)
829                 ulLen = (strlenW(lpDest->Value.lpszW) + 1u) * sizeof(WCHAR);
830             ulCount += ulLen;
831             break;
832         case PT_BINARY:
833             lpDest->Value.bin.lpb = (LPBYTE)RELOC_PTR(lpDest->Value.bin.lpb);
834             ulCount += lpDest->Value.bin.cb;
835             break;
836         default:
837             if (lpDest->ulPropTag & MV_FLAG)
838             {
839                 /* Since we have to access the array elements, don't map the
840                  * array unless it is invalid (otherwise, map it at the end)
841                  */
842                 if (bBadPtr)
843                     lpDest->Value.MVszA.lppszA = (LPSTR*)RELOC_PTR(lpDest->Value.MVszA.lppszA);
844                 
845                 switch (PROP_TYPE(lpProps->ulPropTag))
846                 {
847                 case PT_MV_STRING8:
848                 {
849                     ulCount += lpDest->Value.MVszA.cValues * sizeof(char *);
850
851                     for (i = 0; i < lpDest->Value.MVszA.cValues; i++)
852                     {
853                         ULONG ulStrLen = bBadPtr ? 0 : lstrlenA(lpDest->Value.MVszA.lppszA[i]) + 1u;
854                         
855                         lpDest->Value.MVszA.lppszA[i] = (LPSTR)RELOC_PTR(lpDest->Value.MVszA.lppszA[i]);
856                         if (bBadPtr)
857                             ulStrLen = lstrlenA(lpDest->Value.MVszA.lppszA[i]) + 1u;
858                         ulCount += ulStrLen;
859                     }
860                     break;
861                 }
862                 case PT_MV_UNICODE:
863                 {
864                     ulCount += lpDest->Value.MVszW.cValues * sizeof(WCHAR *);
865                 
866                     for (i = 0; i < lpDest->Value.MVszW.cValues; i++)
867                     {
868                         ULONG ulStrLen = bBadPtr ? 0 : (strlenW(lpDest->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR);
869                     
870                         lpDest->Value.MVszW.lppszW[i] = (LPWSTR)RELOC_PTR(lpDest->Value.MVszW.lppszW[i]);
871                         if (bBadPtr)
872                             ulStrLen = (strlenW(lpDest->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR);
873                         ulCount += ulStrLen;
874                     }                    
875                     break;
876                 }
877                 case PT_MV_BINARY:
878                 {
879                     ulCount += lpDest->Value.MVszW.cValues * sizeof(SBinary);
880                 
881                     for (i = 0; i < lpDest->Value.MVszW.cValues; i++)
882                     {
883                         lpDest->Value.MVbin.lpbin[i].lpb = (LPBYTE)RELOC_PTR(lpDest->Value.MVbin.lpbin[i].lpb);
884                         ulCount += lpDest->Value.MVbin.lpbin[i].cb;
885                     }                    
886                     break;
887                 }
888                 default:
889                     ulCount += UlPropSize(lpDest);
890                     break;
891                 }
892                 if (!bBadPtr)
893                     lpDest->Value.MVszA.lppszA = (LPSTR*)RELOC_PTR(lpDest->Value.MVszA.lppszA);
894                 break;
895             }
896         }
897         lpDest++;
898     }
899     if (lpCount)
900         *lpCount = ulCount;
901         
902     return S_OK;
903 }
904
905 /*************************************************************************
906  * LpValFindProp@12 (MAPI32.173)
907  *
908  * Find a property with a given property id in a property array.
909  *
910  * PARAMS
911  *  ulPropTag [I] Property tag containing property id to find
912  *  cValues   [I] Number of properties in lpProps
913  *  lpProps   [I] Property array to search
914  *
915  * RETURNS
916  *  A pointer to the matching property, or NULL if none was found.
917  *
918  * NOTES
919  *  This function matches only on the property id and does not care if the
920  *  property types differ.
921  */
922 LPSPropValue WINAPI LpValFindProp(ULONG ulPropTag, ULONG cValues, LPSPropValue lpProps)
923 {
924     TRACE("(%ld,%ld,%p)\n", ulPropTag, cValues, lpProps);
925
926     if (lpProps && cValues)
927     {
928         ULONG i;
929         for (i = 0; i < cValues; i++)
930         {
931             if (PROP_ID(ulPropTag) == PROP_ID(lpProps[i].ulPropTag))
932                 return &lpProps[i];
933         }
934     }
935     return NULL;
936 }
937
938 /*************************************************************************
939  * FBadRglpszA@8 (MAPI32.175)
940  *
941  * Determine if an array of strings is invalid
942  *
943  * PARAMS
944  *  lppszStrs [I] Array of strings to check
945  *  ulCount   [I] Number of strings in lppszStrs
946  *
947  * RETURNS
948  *  TRUE, if lppszStrs is invalid, FALSE otherwise.
949  */
950 BOOL WINAPI FBadRglpszA(LPSTR *lppszStrs, ULONG ulCount)
951 {
952     ULONG i;
953
954     TRACE("(%p,%ld)\n", lppszStrs, ulCount);
955
956     if (!ulCount)
957         return FALSE;
958         
959     if (!lppszStrs || IsBadReadPtr(lppszStrs, ulCount * sizeof(LPWSTR)))
960         return TRUE;
961
962     for (i = 0; i < ulCount; i++)
963     {
964         if (!lppszStrs[i] || IsBadStringPtrA(lppszStrs[i], -1))
965             return TRUE;
966     }
967     return FALSE;
968 }
969
970 /*************************************************************************
971  * FBadRglpszW@8 (MAPI32.176)
972  *
973  * See FBadRglpszA.
974  */
975 BOOL WINAPI FBadRglpszW(LPWSTR *lppszStrs, ULONG ulCount)
976 {
977     ULONG i;
978
979     TRACE("(%p,%ld)\n", lppszStrs, ulCount);
980
981     if (!ulCount)
982         return FALSE;
983  
984     if (!lppszStrs || IsBadReadPtr(lppszStrs, ulCount * sizeof(LPWSTR)))
985         return TRUE;
986
987     for (i = 0; i < ulCount; i++)
988     {
989         if (!lppszStrs[i] || IsBadStringPtrW(lppszStrs[i], -1))
990             return TRUE;
991     }
992     return FALSE;
993 }
994
995 /*************************************************************************
996  * FBadRowSet@4 (MAPI32.177)
997  *
998  * Determine if a row is invalid
999  *
1000  * PARAMS
1001  *  lpRow [I] Row to check
1002  *
1003  * RETURNS
1004  *  TRUE, if lpRow is invalid, FALSE otherwise.
1005  */
1006 BOOL WINAPI FBadRowSet(LPSRowSet lpRowSet)
1007 {
1008     ULONG i;
1009     TRACE("(%p)\n", lpRowSet);
1010
1011     if (!lpRowSet || IsBadReadPtr(lpRowSet, CbSRowSet(lpRowSet)))
1012         return TRUE;
1013
1014     for (i = 0; i < lpRowSet->cRows; i++)
1015     {
1016         if (FBadRow(&lpRowSet->aRow[i]))
1017             return TRUE;
1018     }
1019     return FALSE;
1020 }
1021
1022 /*************************************************************************
1023  * FBadPropTag@4 (MAPI32.179)
1024  *
1025  * Determine if a property tag is invalid
1026  *
1027  * PARAMS
1028  *  ulPropTag [I] Property tag to check
1029  *
1030  * RETURNS
1031  *  TRUE, if ulPropTag is invalid, FALSE otherwise.
1032  */
1033 ULONG WINAPI FBadPropTag(ULONG ulPropTag)
1034 {
1035     TRACE("(0x%08lx)\n", ulPropTag);
1036
1037     switch (ulPropTag & (~MV_FLAG & PROP_TYPE_MASK))
1038     {
1039     case PT_UNSPECIFIED:
1040     case PT_NULL:
1041     case PT_I2:
1042     case PT_LONG:
1043     case PT_R4:
1044     case PT_DOUBLE:
1045     case PT_CURRENCY:
1046     case PT_APPTIME:
1047     case PT_ERROR:
1048     case PT_BOOLEAN:
1049     case PT_OBJECT:
1050     case PT_I8:
1051     case PT_STRING8:
1052     case PT_UNICODE:
1053     case PT_SYSTIME:
1054     case PT_CLSID:
1055     case PT_BINARY:
1056         return FALSE;
1057     }
1058     return TRUE;
1059 }
1060
1061 /*************************************************************************
1062  * FBadRow@4 (MAPI32.180)
1063  *
1064  * Determine if a row is invalid
1065  *
1066  * PARAMS
1067  *  lpRow [I] Row to check
1068  *
1069  * RETURNS
1070  *  TRUE, if lpRow is invalid, FALSE otherwise.
1071  */
1072 ULONG WINAPI FBadRow(LPSRow lpRow)
1073 {
1074     ULONG i;
1075     TRACE("(%p)\n", lpRow);
1076
1077     if (!lpRow || IsBadReadPtr(lpRow, sizeof(SRow)) || !lpRow->lpProps ||
1078         IsBadReadPtr(lpRow->lpProps, lpRow->cValues * sizeof(SPropValue)))
1079         return TRUE;
1080
1081     for (i = 0; i < lpRow->cValues; i++)
1082     {
1083         if (FBadProp(&lpRow->lpProps[i]))
1084             return TRUE;
1085     }
1086     return FALSE;
1087 }
1088
1089 /*************************************************************************
1090  * FBadProp@4 (MAPI32.181)
1091  *
1092  * Determine if a property is invalid
1093  *
1094  * PARAMS
1095  *  lpProp [I] Property to check
1096  *
1097  * RETURNS
1098  *  TRUE, if lpProp is invalid, FALSE otherwise.
1099  */
1100 ULONG WINAPI FBadProp(LPSPropValue lpProp)
1101 {
1102     ULONG i;
1103
1104     if (!lpProp || IsBadReadPtr(lpProp, sizeof(SPropValue)) ||
1105         FBadPropTag(lpProp->ulPropTag))
1106         return TRUE;
1107
1108     switch (PROP_TYPE(lpProp->ulPropTag))
1109     {
1110     /* Single value properties containing pointers */
1111     case PT_STRING8:
1112         if (!lpProp->Value.lpszA || IsBadStringPtrA(lpProp->Value.lpszA, -1))
1113             return TRUE;
1114         break;
1115     case PT_UNICODE:
1116         if (!lpProp->Value.lpszW || IsBadStringPtrW(lpProp->Value.lpszW, -1))
1117             return TRUE;
1118         break;
1119     case PT_BINARY:
1120         if (IsBadReadPtr(lpProp->Value.bin.lpb, lpProp->Value.bin.cb))
1121             return TRUE;
1122         break;
1123     case PT_CLSID:
1124         if (IsBadReadPtr(lpProp->Value.lpguid, sizeof(GUID)))
1125             return TRUE;
1126         break;
1127
1128     /* Multiple value properties (arrays) containing no pointers */
1129     case PT_MV_I2:
1130         return PROP_BadArray(lpProp, sizeof(SHORT));
1131     case PT_MV_LONG:
1132         return PROP_BadArray(lpProp, sizeof(LONG));
1133     case PT_MV_LONGLONG:
1134         return PROP_BadArray(lpProp, sizeof(LONG64));
1135     case PT_MV_FLOAT:
1136         return PROP_BadArray(lpProp, sizeof(float));
1137     case PT_MV_SYSTIME:
1138         return PROP_BadArray(lpProp, sizeof(FILETIME));
1139     case PT_MV_APPTIME:
1140     case PT_MV_DOUBLE:
1141         return PROP_BadArray(lpProp, sizeof(double));
1142     case PT_MV_CURRENCY:
1143         return PROP_BadArray(lpProp, sizeof(CY));
1144     case PT_MV_CLSID:
1145         return PROP_BadArray(lpProp, sizeof(GUID));
1146
1147     /* Multiple value properties containing pointers */
1148     case PT_MV_STRING8:
1149         return FBadRglpszA(lpProp->Value.MVszA.lppszA,
1150                            lpProp->Value.MVszA.cValues);
1151     case PT_MV_UNICODE:
1152         return FBadRglpszW(lpProp->Value.MVszW.lppszW,
1153                            lpProp->Value.MVszW.cValues);
1154     case PT_MV_BINARY:
1155         if (PROP_BadArray(lpProp, sizeof(SBinary)))
1156             return TRUE;
1157
1158         for (i = 0; i < lpProp->Value.MVszW.cValues; i++)
1159         {
1160             if (IsBadReadPtr(lpProp->Value.MVbin.lpbin[i].lpb,
1161                              lpProp->Value.MVbin.lpbin[i].cb))
1162                 return TRUE;
1163         }
1164         break;
1165     }
1166     return FALSE;
1167 }
1168
1169 /*************************************************************************
1170  * FBadColumnSet@4 (MAPI32.182)
1171  *
1172  * Determine if an array of property tags is invalid
1173  *
1174  * PARAMS
1175  *  lpCols [I] Property tag array to check
1176  *
1177  * RETURNS
1178  *  TRUE, if lpCols is invalid, FALSE otherwise.
1179  */
1180 ULONG WINAPI FBadColumnSet(LPSPropTagArray lpCols)
1181 {
1182     ULONG ulRet = FALSE, i;
1183
1184     TRACE("(%p)\n", lpCols);
1185
1186     if (!lpCols || IsBadReadPtr(lpCols, CbSPropTagArray(lpCols)))
1187         ulRet = TRUE;
1188     else
1189     {
1190         for (i = 0; i < lpCols->cValues; i++)
1191         {
1192             if ((lpCols->aulPropTag[i] & PROP_TYPE_MASK) == PT_ERROR ||
1193                 FBadPropTag(lpCols->aulPropTag[i]))
1194             {
1195                 ulRet = TRUE;
1196                 break;
1197             }
1198         }
1199     }
1200     TRACE("Returning %s\n", ulRet ? "TRUE" : "FALSE");
1201     return ulRet;
1202 }