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