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