janitorial: Remove remaining NULL checks before free() (found by Smatch).
[wine] / dlls / mapi32 / tests / prop.c
1 /*
2  * Unit test suite for MAPI 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 #define NONAMELESSUNION
22 #define NONAMELESSSTRUCT
23 #include "wine/test.h"
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winuser.h"
27 #include "winerror.h"
28 #include "winnt.h"
29 #include "mapiutil.h"
30 #include "mapitags.h"
31
32 static HMODULE hMapi32 = 0;
33
34 static SCODE        (WINAPI *pScInitMapiUtil)(ULONG);
35 static SCODE        (WINAPI *pPropCopyMore)(LPSPropValue,LPSPropValue,ALLOCATEMORE*,LPVOID);
36 static ULONG        (WINAPI *pUlPropSize)(LPSPropValue);
37 static BOOL         (WINAPI *pFPropContainsProp)(LPSPropValue,LPSPropValue,ULONG);
38 static BOOL         (WINAPI *pFPropCompareProp)(LPSPropValue,ULONG,LPSPropValue);
39 static LONG         (WINAPI *pLPropCompareProp)(LPSPropValue,LPSPropValue);
40 static LPSPropValue (WINAPI *pPpropFindProp)(LPSPropValue,ULONG,ULONG);
41 static SCODE        (WINAPI *pScCountProps)(INT,LPSPropValue,ULONG*);
42 static SCODE        (WINAPI *pScCopyProps)(int,LPSPropValue,LPVOID,ULONG*);
43 static SCODE        (WINAPI *pScRelocProps)(int,LPSPropValue,LPVOID,LPVOID,ULONG*);
44 static LPSPropValue (WINAPI *pLpValFindProp)(ULONG,ULONG,LPSPropValue);
45 static BOOL         (WINAPI *pFBadRglpszA)(LPSTR*,ULONG);
46 static BOOL         (WINAPI *pFBadRglpszW)(LPWSTR*,ULONG);
47 static BOOL         (WINAPI *pFBadRowSet)(LPSRowSet);
48 static ULONG        (WINAPI *pFBadPropTag)(ULONG);
49 static ULONG        (WINAPI *pFBadRow)(LPSRow);
50 static ULONG        (WINAPI *pFBadProp)(LPSPropValue);
51 static ULONG        (WINAPI *pFBadColumnSet)(LPSPropTagArray);
52 static SCODE        (WINAPI *pCreateIProp)(LPCIID,ALLOCATEBUFFER*,ALLOCATEMORE*,
53                                            FREEBUFFER*,LPVOID,LPPROPDATA*);
54 static SCODE        (WINAPI *pMAPIAllocateBuffer)(ULONG, LPVOID);
55 static SCODE        (WINAPI *pMAPIAllocateMore)(ULONG, LPVOID, LPVOID);
56 static SCODE        (WINAPI *pMAPIFreeBuffer)(LPVOID);
57
58 static BOOL InitFuncPtrs(void)
59 {
60     hMapi32 = LoadLibraryA("mapi32.dll");
61
62     pScInitMapiUtil = (void*)GetProcAddress(hMapi32, "ScInitMapiUtil@4");
63     pMAPIAllocateBuffer = (void*)GetProcAddress(hMapi32, "MAPIAllocateBuffer");
64     pMAPIAllocateMore = (void*)GetProcAddress(hMapi32, "MAPIAllocateMore");
65     pMAPIFreeBuffer = (void*)GetProcAddress(hMapi32, "MAPIFreeBuffer");
66     if(pScInitMapiUtil && pMAPIAllocateBuffer && pMAPIAllocateMore && pMAPIFreeBuffer)
67         return TRUE;
68     else
69         return FALSE;
70 }
71
72 static ULONG ptTypes[] = {
73     PT_I2, PT_I4, PT_R4, PT_R8, PT_CURRENCY, PT_APPTIME, PT_SYSTIME,
74     PT_ERROR, PT_BOOLEAN, PT_I8, PT_CLSID, PT_STRING8, PT_BINARY,
75     PT_UNICODE
76 };
77
78 static inline int strcmpW(const WCHAR *str1, const WCHAR *str2)
79 {
80     while (*str1 && (*str1 == *str2)) { str1++; str2++; }
81     return *str1 - *str2;
82 }
83
84 static void test_PropCopyMore(void)
85 {
86     static const char *szHiA = "Hi!";
87     static const WCHAR szHiW[] = { 'H', 'i', '!', '\0' };
88     SPropValue *lpDest = NULL, *lpSrc = NULL;
89     ULONG i;
90     SCODE scode;
91
92     pPropCopyMore = (void*)GetProcAddress(hMapi32, "PropCopyMore@16");
93
94     if (!pPropCopyMore)
95         return;
96
97     scode = pMAPIAllocateBuffer(sizeof(LPSPropValue), (LPVOID *)lpDest);
98     if (FAILED(scode))
99         return;
100
101     scode = pMAPIAllocateMore(sizeof(LPSPropValue), lpDest, (LPVOID *)lpSrc);
102     if (FAILED(scode))
103         return;
104
105     for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++)
106     {
107         lpSrc->ulPropTag = ptTypes[i];
108
109         switch (ptTypes[i])
110         {
111         case PT_STRING8:
112             lpSrc->Value.lpszA = (char*)szHiA;
113             break;
114         case PT_UNICODE:
115             lpSrc->Value.lpszW = (WCHAR*)szHiW;
116             break;
117         case PT_BINARY:
118             lpSrc->Value.bin.cb = 4;
119             lpSrc->Value.bin.lpb = (LPBYTE)szHiA;
120             break;
121         }
122
123         memset(lpDest, 0xff, sizeof(SPropValue));
124
125         scode = pPropCopyMore(lpDest, lpSrc, (ALLOCATEMORE*)pMAPIAllocateMore, lpDest);
126         ok(!scode && lpDest->ulPropTag == lpSrc->ulPropTag,
127            "PropCopyMore: Expected 0x0,%ld, got 0x%08lx,%ld\n",
128            lpSrc->ulPropTag, scode, lpDest->ulPropTag);
129         if (SUCCEEDED(scode))
130         {
131             switch (ptTypes[i])
132             {
133             case PT_STRING8:
134                 ok(lstrcmpA(lpDest->Value.lpszA, lpSrc->Value.lpszA) == 0,
135                    "PropCopyMore: Ascii string differs\n");
136                 break;
137             case PT_UNICODE:
138                 ok(strcmpW(lpDest->Value.lpszW, lpSrc->Value.lpszW) == 0,
139                    "PropCopyMore: Unicode string differs\n");
140                 break;
141             case PT_BINARY:
142                 ok(lpDest->Value.bin.cb == 4 &&
143                    !memcmp(lpSrc->Value.bin.lpb, lpDest->Value.bin.lpb, 4),
144                    "PropCopyMore: Binary array  differs\n");
145                 break;
146             }
147         }
148     }
149
150     /* Since all allocations are linked, freeing lpDest frees everything */
151     pMAPIFreeBuffer(lpDest);
152 }
153
154 static void test_UlPropSize(void)
155 {
156     static const char *szHiA = "Hi!";
157     static const WCHAR szHiW[] = { 'H', 'i', '!', '\0' };
158     LPSTR  buffa[2];
159     LPWSTR buffw[2];
160     SBinary buffbin[2];
161     ULONG pt, exp, res;
162
163     pUlPropSize = (void*)GetProcAddress(hMapi32, "UlPropSize@4");
164
165     if (!pUlPropSize)
166         return;
167
168     for (pt = 0; pt < PROP_ID_INVALID; pt++)
169     {
170         SPropValue pv;
171
172         memset(&pv, 0 ,sizeof(pv));
173         pv.ulPropTag = pt;
174
175         exp = 1u; /* Default to one item for non-MV properties */
176
177         switch (PROP_TYPE(pt))
178         {
179         case PT_MV_I2:       pv.Value.MVi.cValues = exp = 2;
180         case PT_I2:          exp *= sizeof(USHORT); break;
181         case PT_MV_I4:       pv.Value.MVl.cValues = exp = 2;
182         case PT_I4:          exp *= sizeof(LONG); break;
183         case PT_MV_R4:       pv.Value.MVflt.cValues = exp = 2;
184         case PT_R4:          exp *= sizeof(float); break;
185         case PT_MV_DOUBLE:   pv.Value.MVdbl.cValues = exp = 2;
186         case PT_R8:          exp *= sizeof(double); break;
187         case PT_MV_CURRENCY: pv.Value.MVcur.cValues = exp = 2;
188         case PT_CURRENCY:    exp *= sizeof(CY); break;
189         case PT_MV_APPTIME:  pv.Value.MVat.cValues = exp = 2;
190         case PT_APPTIME:     exp *= sizeof(double); break;
191         case PT_MV_SYSTIME:  pv.Value.MVft.cValues = exp = 2;
192         case PT_SYSTIME:     exp *= sizeof(FILETIME); break;
193         case PT_ERROR:       exp = sizeof(SCODE); break;
194         case PT_BOOLEAN:     exp = sizeof(USHORT); break;
195         case PT_OBJECT:      exp = 0; break;
196         case PT_MV_I8:       pv.Value.MVli.cValues = exp = 2;
197         case PT_I8:          exp *= sizeof(LONG64); break;
198 #if 0
199         /* My version of native mapi returns 0 for PT_MV_CLSID even if a valid
200          * array is given. This _has_ to be a bug, so Wine does
201          * the right thing(tm) and we don't test it here.
202          */
203         case PT_MV_CLSID:    pv.Value.MVguid.cValues = exp = 2;
204 #endif
205         case PT_CLSID:       exp *= sizeof(GUID); break;
206         case PT_STRING8:
207             pv.Value.lpszA = (LPSTR)szHiA;
208             exp = 4;
209             break;
210         case PT_UNICODE:
211             pv.Value.lpszW = (LPWSTR)szHiW;
212             exp = 4 * sizeof(WCHAR);
213             break;
214         case PT_BINARY:
215             pv.Value.bin.cb = exp = 19;
216             break;
217         case PT_MV_STRING8:
218             pv.Value.MVszA.cValues = 2;
219             pv.Value.MVszA.lppszA = buffa;
220             buffa[0] = (LPSTR)szHiA;
221             buffa[1] = (LPSTR)szHiA;
222             exp = 8;
223             break;
224         case PT_MV_UNICODE:
225             pv.Value.MVszW.cValues = 2;
226             pv.Value.MVszW.lppszW = buffw;
227             buffw[0] = (LPWSTR)szHiW;
228             buffw[1] = (LPWSTR)szHiW;
229             exp = 8 * sizeof(WCHAR);
230             break;
231         case PT_MV_BINARY:
232             pv.Value.MVbin.cValues = 2;
233             pv.Value.MVbin.lpbin = buffbin;
234             buffbin[0].cb = 19;
235             buffbin[1].cb = 1;
236             exp = 20;
237             break;
238         default:
239             exp = 0;
240         }
241
242         res = pUlPropSize(&pv);
243         ok(res == exp, "pt= %ld: Expected %ld, got %ld\n", pt, exp, res);
244     }
245 }
246
247 static void test_FPropContainsProp(void)
248 {
249     static const char *szFull = "Full String";
250     static const char *szFullLower = "full string";
251     static const char *szPrefix = "Full";
252     static const char *szPrefixLower = "full";
253     static const char *szSubstring = "ll St";
254     static const char *szSubstringLower = "ll st";
255     SPropValue pvLeft, pvRight;
256     ULONG pt;
257     BOOL bRet;
258
259     pFPropContainsProp = (void*)GetProcAddress(hMapi32, "FPropContainsProp@12");
260
261     if (!pFPropContainsProp)
262         return;
263
264     /* Ensure that only PT_STRING8 and PT_BINARY are handled */
265     for (pt = 0; pt < PROP_ID_INVALID; pt++)
266     {
267         if (pt == PT_STRING8 || pt == PT_BINARY)
268             continue; /* test these later */
269
270         memset(&pvLeft, 0 ,sizeof(pvLeft));
271         memset(&pvRight, 0 ,sizeof(pvRight));
272         pvLeft.ulPropTag = pvRight.ulPropTag = pt;
273
274         bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
275         ok(bRet == FALSE, "pt= %ld: Expected FALSE, got %d\n", pt, bRet);
276     }
277
278     /* test the various flag combinations */
279     pvLeft.ulPropTag = pvRight.ulPropTag = PT_STRING8;
280     pvLeft.Value.lpszA = (LPSTR)szFull;
281     pvRight.Value.lpszA = (LPSTR)szFull;
282
283     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
284     ok(bRet == TRUE, "(full,full)[] match failed\n");
285     pvRight.Value.lpszA = (LPSTR)szPrefix;
286     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
287     ok(bRet == FALSE, "(full,prefix)[] match failed\n");
288     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
289     ok(bRet == TRUE, "(full,prefix)[PREFIX] match failed\n");
290     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
291     ok(bRet == TRUE, "(full,prefix)[SUBSTRING] match failed\n");
292     pvRight.Value.lpszA = (LPSTR)szPrefixLower;
293     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
294     ok(bRet == FALSE, "(full,prefixlow)[PREFIX] match failed\n");
295     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
296     ok(bRet == FALSE, "(full,prefixlow)[SUBSTRING] match failed\n");
297     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX|FL_IGNORECASE);
298     ok(bRet == TRUE, "(full,prefixlow)[PREFIX|IGNORECASE] match failed\n");
299     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING|FL_IGNORECASE);
300     ok(bRet == TRUE, "(full,prefixlow)[SUBSTRING|IGNORECASE] match failed\n");
301     pvRight.Value.lpszA = (LPSTR)szSubstring;
302     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
303     ok(bRet == FALSE, "(full,substr)[] match failed\n");
304     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
305     ok(bRet == FALSE, "(full,substr)[PREFIX] match failed\n");
306     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
307     ok(bRet == TRUE, "(full,substr)[SUBSTRING] match failed\n");
308     pvRight.Value.lpszA = (LPSTR)szSubstringLower;
309     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
310     ok(bRet == FALSE, "(full,substrlow)[PREFIX] match failed\n");
311     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
312     ok(bRet == FALSE, "(full,substrlow)[SUBSTRING] match failed\n");
313     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX|FL_IGNORECASE);
314     ok(bRet == FALSE, "(full,substrlow)[PREFIX|IGNORECASE] match failed\n");
315     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING|FL_IGNORECASE);
316     ok(bRet == TRUE, "(full,substrlow)[SUBSTRING|IGNORECASE] match failed\n");
317     pvRight.Value.lpszA = (LPSTR)szFullLower;
318     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING|FL_IGNORECASE);
319     ok(bRet == TRUE, "(full,fulllow)[IGNORECASE] match failed\n");
320
321     pvLeft.ulPropTag = pvRight.ulPropTag = PT_BINARY;
322     pvLeft.Value.bin.lpb = (LPBYTE)szFull;
323     pvRight.Value.bin.lpb = (LPBYTE)szFull;
324     pvLeft.Value.bin.cb = pvRight.Value.bin.cb = strlen(szFull);
325
326     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
327     ok(bRet == TRUE, "bin(full,full)[] match failed\n");
328     pvRight.Value.bin.lpb = (LPBYTE)szPrefix;
329     pvRight.Value.bin.cb = strlen(szPrefix);
330     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
331     ok(bRet == FALSE, "bin(full,prefix)[] match failed\n");
332     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
333     ok(bRet == TRUE, "bin(full,prefix)[PREFIX] match failed\n");
334     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
335     ok(bRet == TRUE, "bin(full,prefix)[SUBSTRING] match failed\n");
336     pvRight.Value.bin.lpb = (LPBYTE)szPrefixLower;
337     pvRight.Value.bin.cb = strlen(szPrefixLower);
338     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
339     ok(bRet == FALSE, "bin(full,prefixlow)[PREFIX] match failed\n");
340     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
341     ok(bRet == FALSE, "bin(full,prefixlow)[SUBSTRING] match failed\n");
342     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX|FL_IGNORECASE);
343     ok(bRet == FALSE, "bin(full,prefixlow)[PREFIX|IGNORECASE] match failed\n");
344     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING|FL_IGNORECASE);
345     ok(bRet == FALSE, "bin(full,prefixlow)[SUBSTRING|IGNORECASE] match failed\n");
346     pvRight.Value.bin.lpb = (LPBYTE)szSubstring;
347     pvRight.Value.bin.cb = strlen(szSubstring);
348     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
349     ok(bRet == FALSE, "bin(full,substr)[] match failed\n");
350     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
351     ok(bRet == FALSE, "bin(full,substr)[PREFIX] match failed\n");
352     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
353     ok(bRet == TRUE, "bin(full,substr)[SUBSTRING] match failed\n");
354     pvRight.Value.bin.lpb = (LPBYTE)szSubstringLower;
355     pvRight.Value.bin.cb = strlen(szSubstringLower);
356     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
357     ok(bRet == FALSE, "bin(full,substrlow)[PREFIX] match failed\n");
358     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
359     ok(bRet == FALSE, "bin(full,substrlow)[SUBSTRING] match failed\n");
360     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX|FL_IGNORECASE);
361     ok(bRet == FALSE, "bin(full,substrlow)[PREFIX|IGNORECASE] match failed\n");
362     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING|FL_IGNORECASE);
363     ok(bRet == FALSE, "bin(full,substrlow)[SUBSTRING|IGNORECASE] match failed\n");
364     pvRight.Value.bin.lpb = (LPBYTE)szFullLower;
365     pvRight.Value.bin.cb = strlen(szFullLower);
366     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING|FL_IGNORECASE);
367     ok(bRet == FALSE, "bin(full,fulllow)[IGNORECASE] match failed\n");
368 }
369
370 typedef struct tagFPropCompareProp_Result
371 {
372     SHORT lVal;
373     SHORT rVal;
374     ULONG relOp;
375     BOOL  bRet;
376 } FPropCompareProp_Result;
377
378 static const FPropCompareProp_Result FPCProp_Results[] =
379 {
380     { 1, 2, RELOP_LT, TRUE },
381     { 1, 1, RELOP_LT, FALSE },
382     { 2, 1, RELOP_LT, FALSE },
383     { 1, 2, RELOP_LE, TRUE },
384     { 1, 1, RELOP_LE, TRUE },
385     { 2, 1, RELOP_LE, FALSE },
386     { 1, 2, RELOP_GT, FALSE },
387     { 1, 1, RELOP_GT, FALSE },
388     { 2, 1, RELOP_GT, TRUE },
389     { 1, 2, RELOP_GE, FALSE },
390     { 1, 1, RELOP_GE, TRUE },
391     { 2, 1, RELOP_GE, TRUE },
392     { 1, 2, RELOP_EQ, FALSE },
393     { 1, 1, RELOP_EQ, TRUE },
394     { 2, 1, RELOP_EQ, FALSE }
395 };
396
397 static const char *relops[] = { "RELOP_LT", "RELOP_LE", "RELOP_GT", "RELOP_GE", "RELOP_EQ" };
398
399 static void test_FPropCompareProp(void)
400 {
401     SPropValue pvLeft, pvRight;
402     GUID lguid, rguid;
403     char lbuffa[2], rbuffa[2];
404     WCHAR lbuffw[2], rbuffw[2];
405     ULONG i, j;
406     BOOL bRet, bExp;
407
408     pFPropCompareProp = (void*)GetProcAddress(hMapi32, "FPropCompareProp@12");
409
410     if (!pFPropCompareProp)
411         return;
412
413     lbuffa[1] = '\0';
414     rbuffa[1] = '\0';
415     lbuffw[1] = '\0';
416     rbuffw[1] = '\0';
417
418     for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++)
419     {
420         pvLeft.ulPropTag = pvRight.ulPropTag = ptTypes[i];
421
422         for (j = 0; j < sizeof(FPCProp_Results)/sizeof(FPCProp_Results[0]); j++)
423         {
424             SHORT lVal = FPCProp_Results[j].lVal;
425             SHORT rVal = FPCProp_Results[j].rVal;
426
427             bExp = FPCProp_Results[j].bRet;
428
429             switch (ptTypes[i])
430             {
431             case PT_BOOLEAN:
432                 /* Boolean values have no concept of less or greater than, only equality */
433                 if ((lVal == 1 && rVal == 2 && FPCProp_Results[j].relOp == RELOP_LT) ||
434                     (lVal == 2 && rVal == 1 && FPCProp_Results[j].relOp == RELOP_LE)||
435                     (lVal == 2 && rVal == 1 && FPCProp_Results[j].relOp == RELOP_GT)||
436                     (lVal == 1 && rVal == 2 && FPCProp_Results[j].relOp == RELOP_GE)||
437                     (lVal == 1 && rVal == 2 && FPCProp_Results[j].relOp == RELOP_EQ)||
438                     (lVal == 2 && rVal == 1 && FPCProp_Results[j].relOp == RELOP_EQ))
439                     bExp = !bExp;
440                     /* Fall through ... */
441             case PT_I2:
442                 pvLeft.Value.i = lVal;
443                 pvRight.Value.i = rVal;
444                 break;
445             case PT_ERROR:
446             case PT_I4:
447                 pvLeft.Value.l = lVal;
448                 pvRight.Value.l = rVal;
449                 break;
450             case PT_R4:
451                 pvLeft.Value.flt = lVal;
452                 pvRight.Value.flt = rVal;
453                 break;
454             case PT_APPTIME:
455             case PT_R8:
456                 pvLeft.Value.dbl = lVal;
457                 pvRight.Value.dbl = rVal;
458                 break;
459             case PT_CURRENCY:
460                 pvLeft.Value.cur.int64 = lVal;
461                 pvRight.Value.cur.int64 = rVal;
462                 break;
463             case PT_SYSTIME:
464                 pvLeft.Value.ft.dwLowDateTime = lVal;
465                 pvLeft.Value.ft.dwHighDateTime = 0;
466                 pvRight.Value.ft.dwLowDateTime = rVal;
467                 pvRight.Value.ft.dwHighDateTime = 0;
468                 break;
469             case PT_I8:
470                 pvLeft.Value.li.u.LowPart = lVal;
471                 pvLeft.Value.li.u.HighPart = 0;
472                 pvRight.Value.li.u.LowPart = rVal;
473                 pvRight.Value.li.u.HighPart = 0;
474                 break;
475             case PT_CLSID:
476                 memset(&lguid, 0, sizeof(GUID));
477                 memset(&rguid, 0, sizeof(GUID));
478                 lguid.Data4[7] = lVal;
479                 rguid.Data4[7] = rVal;
480                 pvLeft.Value.lpguid = &lguid;
481                 pvRight.Value.lpguid = &rguid;
482                 break;
483             case PT_STRING8:
484                 pvLeft.Value.lpszA = lbuffa;
485                 pvRight.Value.lpszA = rbuffa;
486                 lbuffa[0] = '0' + lVal;
487                 rbuffa[0] = '0' + rVal;
488                 break;
489             case PT_UNICODE:
490                 pvLeft.Value.lpszW = lbuffw;
491                 pvRight.Value.lpszW = rbuffw;
492                 lbuffw[0] = '0' + lVal;
493                 rbuffw[0] = '0' + rVal;
494                 break;
495             case PT_BINARY:
496                 pvLeft.Value.bin.cb = 1;
497                 pvRight.Value.bin.cb = 1;
498                 pvLeft.Value.bin.lpb = (LPBYTE)lbuffa;
499                 pvRight.Value.bin.lpb = (LPBYTE)rbuffa;
500                 lbuffa[0] = lVal;
501                 rbuffa[0] = rVal;
502                 break;
503             }
504
505             bRet = pFPropCompareProp(&pvLeft, FPCProp_Results[j].relOp, &pvRight);
506             ok(bRet == bExp, "pt %ld (%d,%d,%s): expected %d, got %d\n", ptTypes[i],
507                FPCProp_Results[j].lVal, FPCProp_Results[j].rVal,
508                relops[FPCProp_Results[j].relOp], bExp, bRet);
509         }
510     }
511 }
512
513 typedef struct tagLPropCompareProp_Result
514 {
515     SHORT lVal;
516     SHORT rVal;
517     INT   iRet;
518 } LPropCompareProp_Result;
519
520 static const LPropCompareProp_Result LPCProp_Results[] =
521 {
522     { 1, 2, -1 },
523     { 1, 1, 0 },
524     { 2, 1, 1 },
525 };
526
527 static void test_LPropCompareProp(void)
528 {
529     SPropValue pvLeft, pvRight;
530     GUID lguid, rguid;
531     char lbuffa[2], rbuffa[2];
532     WCHAR lbuffw[2], rbuffw[2];
533     ULONG i, j;
534     INT iRet, iExp;
535
536     pLPropCompareProp = (void*)GetProcAddress(hMapi32, "LPropCompareProp@8");
537
538     if (!pLPropCompareProp)
539         return;
540
541     lbuffa[1] = '\0';
542     rbuffa[1] = '\0';
543     lbuffw[1] = '\0';
544     rbuffw[1] = '\0';
545
546     for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++)
547     {
548         pvLeft.ulPropTag = pvRight.ulPropTag = ptTypes[i];
549
550         for (j = 0; j < sizeof(LPCProp_Results)/sizeof(LPCProp_Results[0]); j++)
551         {
552             SHORT lVal = LPCProp_Results[j].lVal;
553             SHORT rVal = LPCProp_Results[j].rVal;
554
555             iExp = LPCProp_Results[j].iRet;
556
557             switch (ptTypes[i])
558             {
559             case PT_BOOLEAN:
560                 /* Boolean values have no concept of less or greater than, only equality */
561                 if (lVal && rVal)
562                     iExp = 0;
563                     /* Fall through ... */
564             case PT_I2:
565                 pvLeft.Value.i = lVal;
566                 pvRight.Value.i = rVal;
567                 break;
568             case PT_ERROR:
569             case PT_I4:
570                 pvLeft.Value.l = lVal;
571                 pvRight.Value.l = rVal;
572                 break;
573             case PT_R4:
574                 pvLeft.Value.flt = lVal;
575                 pvRight.Value.flt = rVal;
576                 break;
577             case PT_APPTIME:
578             case PT_R8:
579                 pvLeft.Value.dbl = lVal;
580                 pvRight.Value.dbl = rVal;
581                 break;
582             case PT_CURRENCY:
583                 pvLeft.Value.cur.int64 = lVal;
584                 pvRight.Value.cur.int64 = rVal;
585                 break;
586             case PT_SYSTIME:
587                 pvLeft.Value.ft.dwLowDateTime = lVal;
588                 pvLeft.Value.ft.dwHighDateTime = 0;
589                 pvRight.Value.ft.dwLowDateTime = rVal;
590                 pvRight.Value.ft.dwHighDateTime = 0;
591                 break;
592             case PT_I8:
593                 pvLeft.Value.li.u.LowPart = lVal;
594                 pvLeft.Value.li.u.HighPart = 0;
595                 pvRight.Value.li.u.LowPart = rVal;
596                 pvRight.Value.li.u.HighPart = 0;
597                 break;
598             case PT_CLSID:
599                 memset(&lguid, 0, sizeof(GUID));
600                 memset(&rguid, 0, sizeof(GUID));
601                 lguid.Data4[7] = lVal;
602                 rguid.Data4[7] = rVal;
603                 pvLeft.Value.lpguid = &lguid;
604                 pvRight.Value.lpguid = &rguid;
605                 break;
606             case PT_STRING8:
607                 pvLeft.Value.lpszA = lbuffa;
608                 pvRight.Value.lpszA = rbuffa;
609                 lbuffa[0] = '0' + lVal;
610                 rbuffa[0] = '0' + rVal;
611                 break;
612             case PT_UNICODE:
613                 pvLeft.Value.lpszW = lbuffw;
614                 pvRight.Value.lpszW = rbuffw;
615                 lbuffw[0] = '0' + lVal;
616                 rbuffw[0] = '0' + rVal;
617                 break;
618             case PT_BINARY:
619                 pvLeft.Value.bin.cb = 1;
620                 pvRight.Value.bin.cb = 1;
621                 pvLeft.Value.bin.lpb = (LPBYTE)lbuffa;
622                 pvRight.Value.bin.lpb = (LPBYTE)rbuffa;
623                 lbuffa[0] = lVal;
624                 rbuffa[0] = rVal;
625                 break;
626             }
627
628             iRet = pLPropCompareProp(&pvLeft, &pvRight);
629             ok(iRet == iExp, "pt %ld (%d,%d): expected %d, got %d\n", ptTypes[i],
630                LPCProp_Results[j].lVal, LPCProp_Results[j].rVal, iExp, iRet);
631         }
632     }
633 }
634
635 static void test_PpropFindProp(void)
636 {
637     SPropValue pvProp, *pRet;
638     ULONG i;
639
640     pPpropFindProp = (void*)GetProcAddress(hMapi32, "PpropFindProp@12");
641
642     if (!pPpropFindProp)
643         return;
644
645     for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++)
646     {
647         pvProp.ulPropTag = ptTypes[i];
648
649         pRet = pPpropFindProp(&pvProp, 1u, ptTypes[i]);
650         ok(pRet == &pvProp, "PpropFindProp[%ld]: Didn't find existing propery\n",
651            ptTypes[i]);
652
653         pRet = pPpropFindProp(&pvProp, 1u, i ? ptTypes[i-1] : ptTypes[i+1]);
654         ok(pRet == NULL, "PpropFindProp[%ld]: Found nonexistent propery\n",
655            ptTypes[i]);
656     }
657
658     pvProp.ulPropTag = PROP_TAG(PT_I2, 1u);
659     pRet = pPpropFindProp(&pvProp, 1u, PROP_TAG(PT_UNSPECIFIED, 0u));
660     ok(pRet == NULL, "PpropFindProp[UNSPECIFIED]: Matched on different id\n");
661     pRet = pPpropFindProp(&pvProp, 1u, PROP_TAG(PT_UNSPECIFIED, 1u));
662     ok(pRet == &pvProp, "PpropFindProp[UNSPECIFIED]: Didn't match id\n");
663 }
664
665 static void test_ScCountProps(void)
666 {
667     static const char *szHiA = "Hi!";
668     static const WCHAR szHiW[] = { 'H', 'i', '!', '\0' };
669     static const ULONG ULHILEN = 4; /* chars in szHiA/W incl. NUL */
670     LPSTR  buffa[3];
671     LPWSTR buffw[3];
672     SBinary buffbin[3];
673     GUID iids[4], *iid = iids;
674     SCODE res;
675     ULONG pt, exp, ulRet;
676     int success = 1;
677
678     pScCountProps = (void*)GetProcAddress(hMapi32, "ScCountProps@12");
679
680     if (!pScCountProps)
681         return;
682
683     for (pt = 0; pt < PROP_ID_INVALID && success; pt++)
684     {
685         SPropValue pv;
686
687         memset(&pv, 0 ,sizeof(pv));
688         pv.ulPropTag = PROP_TAG(pt, 1u);
689
690         switch (PROP_TYPE(pt))
691         {
692         case PT_I2:
693         case PT_I4:
694         case PT_R4:
695         case PT_R8:
696         case PT_CURRENCY:
697         case PT_APPTIME:
698         case PT_SYSTIME:
699         case PT_ERROR:
700         case PT_BOOLEAN:
701         case PT_OBJECT:
702         case PT_I8:
703             exp = sizeof(pv);
704             break;
705         case PT_CLSID:
706             pv.Value.lpguid = iid;
707             exp = sizeof(GUID) + sizeof(pv);
708             break;
709         case PT_STRING8:
710             pv.Value.lpszA = (LPSTR)szHiA;
711             exp = 4 + sizeof(pv);
712             break;
713         case PT_UNICODE:
714             pv.Value.lpszW = (LPWSTR)szHiW;
715             exp = 4 * sizeof(WCHAR) + sizeof(pv);
716             break;
717         case PT_BINARY:
718             pv.Value.bin.cb = 2;
719             pv.Value.bin.lpb = (LPBYTE)iid;
720             exp = 2 + sizeof(pv);
721             break;
722         case PT_MV_I2:
723             pv.Value.MVi.cValues = 3;
724             pv.Value.MVi.lpi = (SHORT*)iid;
725             exp = 3 * sizeof(SHORT) + sizeof(pv);
726             break;
727         case PT_MV_I4:
728             pv.Value.MVl.cValues = 3;
729             pv.Value.MVl.lpl = (LONG*)iid;
730             exp = 3 * sizeof(LONG) + sizeof(pv);
731             break;
732         case PT_MV_I8:
733             pv.Value.MVli.cValues = 3;
734             pv.Value.MVli.lpli = (LARGE_INTEGER*)iid;
735             exp = 3 * sizeof(LARGE_INTEGER) + sizeof(pv);
736             break;
737         case PT_MV_R4:
738             pv.Value.MVflt.cValues = 3;
739             pv.Value.MVflt.lpflt = (float*)iid;
740             exp = 3 * sizeof(float) + sizeof(pv);
741             break;
742         case PT_MV_APPTIME:
743         case PT_MV_R8:
744             pv.Value.MVdbl.cValues = 3;
745             pv.Value.MVdbl.lpdbl = (double*)iid;
746             exp = 3 * sizeof(double) + sizeof(pv);
747             break;
748         case PT_MV_CURRENCY:
749             pv.Value.MVcur.cValues = 3;
750             pv.Value.MVcur.lpcur = (CY*)iid;
751             exp = 3 * sizeof(CY) + sizeof(pv);
752             break;
753         case PT_MV_SYSTIME:
754             pv.Value.MVft.cValues = 3;
755             pv.Value.MVft.lpft = (FILETIME*)iid;
756             exp = 3 * sizeof(CY) + sizeof(pv);
757             break;
758         case PT_MV_STRING8:
759             pv.Value.MVszA.cValues = 3;
760             pv.Value.MVszA.lppszA = buffa;
761             buffa[0] = (LPSTR)szHiA;
762             buffa[1] = (LPSTR)szHiA;
763             buffa[2] = (LPSTR)szHiA;
764             exp = ULHILEN * 3 + 3 * sizeof(char*) + sizeof(pv);
765             break;
766         case PT_MV_UNICODE:
767             pv.Value.MVszW.cValues = 3;
768             pv.Value.MVszW.lppszW = buffw;
769             buffw[0] = (LPWSTR)szHiW;
770             buffw[1] = (LPWSTR)szHiW;
771             buffw[2] = (LPWSTR)szHiW;
772             exp = ULHILEN * 3 * sizeof(WCHAR) + 3 * sizeof(WCHAR*) + sizeof(pv);
773             break;
774         case PT_MV_BINARY:
775             pv.Value.MVbin.cValues = 3;
776             pv.Value.MVbin.lpbin = buffbin;
777             buffbin[0].cb = 17;
778             buffbin[0].lpb = (LPBYTE)&iid;
779             buffbin[1].cb = 2;
780             buffbin[1].lpb = (LPBYTE)&iid;
781             buffbin[2].cb = 1;
782             buffbin[2].lpb = (LPBYTE)&iid;
783             exp = 20 + sizeof(pv) + sizeof(SBinary) * 3;
784             break;
785         default:
786             exp = 0;
787         }
788
789         ulRet = 0xffffffff;
790         res = pScCountProps(1, &pv, &ulRet);
791         if (!exp) {
792             success = res == MAPI_E_INVALID_PARAMETER && ulRet == 0xffffffff;
793             ok(success, "pt= %ld: Expected failure, got %ld, ret=0x%08lX\n",
794                pt, ulRet, res);
795         }
796         else {
797             success = res == S_OK && ulRet == exp;
798             ok(success, "pt= %ld: Expected %ld, got %ld, ret=0x%08lX\n",
799                pt, exp, ulRet, res);
800         }
801     }
802
803 }
804
805 static void test_ScCopyRelocProps(void)
806 {
807     static const char* szTestA = "Test";
808     char buffer[512], buffer2[512], *lppszA[1];
809     SPropValue pvProp, *lpResProp = (LPSPropValue)buffer;
810     ULONG ulCount;
811     SCODE sc;
812
813     pScCopyProps = (void*)GetProcAddress(hMapi32, "ScCopyProps@16");
814     pScRelocProps = (void*)GetProcAddress(hMapi32, "ScRelocProps@20");
815
816     if (!pScCopyProps || !pScRelocProps)
817         return;
818
819     pvProp.ulPropTag = PROP_TAG(PT_MV_STRING8, 1u);
820
821     lppszA[0] = (char *)szTestA;
822     pvProp.Value.MVszA.cValues = 1;
823     pvProp.Value.MVszA.lppszA = lppszA;
824     ulCount = 0;
825
826     sc = pScCopyProps(1, &pvProp, buffer, &ulCount);
827     ok(sc == S_OK, "wrong ret %ld\n", sc);
828     ok(lpResProp->ulPropTag == pvProp.ulPropTag, "wrong tag %lx\n",lpResProp->ulPropTag);
829     ok(lpResProp->Value.MVszA.cValues == 1, "wrong cValues %ld\n", lpResProp->Value.MVszA.cValues);
830     ok(lpResProp->Value.MVszA.lppszA[0] == buffer + sizeof(SPropValue) + sizeof(char*),
831        "wrong lppszA[0] %p\n",lpResProp->Value.MVszA.lppszA[0]);
832     ok(ulCount == sizeof(SPropValue) + sizeof(char*) + 5, "wrong count %ld\n", ulCount);
833     ok(!strcmp(lpResProp->Value.MVszA.lppszA[0], szTestA),
834        "wrong string '%s'\n", lpResProp->Value.MVszA.lppszA[0]);
835
836     memcpy(buffer2, buffer, sizeof(buffer));
837
838     /* Clear the data in the source buffer. Since pointers in the copied buffer
839      * refer to the source buffer, this proves that native always assumes that
840      * the copied buffers pointers are bad (needing to be relocated first).
841      */
842     memset(buffer, 0, sizeof(buffer));
843     ulCount = 0;
844
845     sc = pScRelocProps(1, (LPSPropValue)buffer2, buffer, buffer2, &ulCount);
846     lpResProp = (LPSPropValue)buffer2;
847
848     ok(sc == S_OK, "wrong ret %ld\n", sc);
849     ok(lpResProp->ulPropTag == pvProp.ulPropTag, "wrong tag %lx\n",lpResProp->ulPropTag);
850     ok(lpResProp->Value.MVszA.cValues == 1, "wrong cValues %ld\n", lpResProp->Value.MVszA.cValues);
851     ok(lpResProp->Value.MVszA.lppszA[0] == buffer2 + sizeof(SPropValue) + sizeof(char*),
852        "wrong lppszA[0] %p\n",lpResProp->Value.MVszA.lppszA[0]);
853     /* Native has a bug whereby it calculates the size correctly when copying
854      * but when relocating does not (presumably it uses UlPropSize() which
855      * ignores multivalue pointers). Wine returns the correct value.
856      */
857     ok(ulCount == sizeof(SPropValue) + sizeof(char*) + 5 || ulCount == sizeof(SPropValue) + 5,
858        "wrong count %ld\n", ulCount);
859     ok(!strcmp(lpResProp->Value.MVszA.lppszA[0], szTestA),
860        "wrong string '%s'\n", lpResProp->Value.MVszA.lppszA[0]);
861
862     /* Native crashes with lpNew or lpOld set to NULL so skip testing this */
863 }
864
865 static void test_LpValFindProp(void)
866 {
867     SPropValue pvProp, *pRet;
868     ULONG i;
869
870     pLpValFindProp = (void*)GetProcAddress(hMapi32, "LpValFindProp@12");
871
872     if (!pLpValFindProp)
873         return;
874
875     for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++)
876     {
877         pvProp.ulPropTag = PROP_TAG(ptTypes[i], 1u);
878
879         pRet = pLpValFindProp(PROP_TAG(ptTypes[i], 1u), 1u, &pvProp);
880         ok(pRet == &pvProp, "LpValFindProp[%ld]: Didn't find existing propery id/type\n",
881            ptTypes[i]);
882
883         pRet = pLpValFindProp(PROP_TAG(ptTypes[i], 0u), 1u, &pvProp);
884         ok(pRet == NULL, "LpValFindProp[%ld]: Found nonexistent propery id\n",
885            ptTypes[i]);
886
887         pRet = pLpValFindProp(PROP_TAG(PT_NULL, 0u), 1u, &pvProp);
888         ok(pRet == NULL, "LpValFindProp[%ld]: Found nonexistent propery id/type\n",
889            ptTypes[i]);
890
891         pRet = pLpValFindProp(PROP_TAG(PT_NULL, 1u), 1u, &pvProp);
892         ok(pRet == &pvProp, "LpValFindProp[%ld]: Didn't find existing propery id\n",
893            ptTypes[i]);
894     }
895 }
896
897 static void test_FBadRglpszA(void)
898 {
899     LPSTR lpStrs[4];
900     static CHAR szString[] = "A String";
901     BOOL bRet;
902
903     pFBadRglpszA = (void*)GetProcAddress(hMapi32, "FBadRglpszA@8");
904     if (!pFBadRglpszA)
905         return;
906
907     bRet = pFBadRglpszA(NULL, 10);
908     ok(bRet == TRUE, "FBadRglpszA(Null): expected TRUE, got FALSE\n");
909
910     lpStrs[0] = lpStrs[1] = lpStrs[2] = lpStrs[3] = NULL;
911     bRet = pFBadRglpszA(lpStrs, 4);
912     ok(bRet == TRUE, "FBadRglpszA(Nulls): expected TRUE, got FALSE\n");
913
914     lpStrs[0] = lpStrs[1] = lpStrs[2] = szString;
915     bRet = pFBadRglpszA(lpStrs, 3);
916     ok(bRet == FALSE, "FBadRglpszA(valid): expected FALSE, got TRUE\n");
917
918     bRet = pFBadRglpszA(lpStrs, 4);
919     ok(bRet == TRUE, "FBadRglpszA(1 invalid): expected TRUE, got FALSE\n");
920 }
921
922 static void test_FBadRglpszW(void)
923 {
924     LPWSTR lpStrs[4];
925     static WCHAR szString[] = { 'A',' ','S','t','r','i','n','g','\0' };
926     BOOL bRet;
927
928     pFBadRglpszW = (void*)GetProcAddress(hMapi32, "FBadRglpszW@8");
929     if (!pFBadRglpszW)
930         return;
931
932     bRet = pFBadRglpszW(NULL, 10);
933     ok(bRet == TRUE, "FBadRglpszW(Null): expected TRUE, got FALSE\n");
934
935     lpStrs[0] = lpStrs[1] = lpStrs[2] = lpStrs[3] = NULL;
936     bRet = pFBadRglpszW(lpStrs, 4);
937     ok(bRet == TRUE, "FBadRglpszW(Nulls): expected TRUE, got FALSE\n");
938
939     lpStrs[0] = lpStrs[1] = lpStrs[2] = szString;
940     bRet = pFBadRglpszW(lpStrs, 3);
941     ok(bRet == FALSE, "FBadRglpszW(valid): expected FALSE, got TRUE\n");
942
943     bRet = pFBadRglpszW(lpStrs, 4);
944     ok(bRet == TRUE, "FBadRglpszW(1 invalid): expected TRUE, got FALSE\n");
945 }
946
947 static void test_FBadRowSet(void)
948 {
949     ULONG ulRet;
950
951     pFBadRowSet = (void*)GetProcAddress(hMapi32, "FBadRowSet@4");
952     if (!pFBadRowSet)
953         return;
954
955     ulRet = pFBadRowSet(NULL);
956     ok(ulRet != 0, "FBadRow(null): Expected non-zero, got 0\n");
957
958     /* FIXME */
959 }
960
961 static void test_FBadPropTag(void)
962 {
963     ULONG pt, res;
964
965     pFBadPropTag = (void*)GetProcAddress(hMapi32, "FBadPropTag@4");
966     if (!pFBadPropTag)
967         return;
968
969     for (pt = 0; pt < PROP_ID_INVALID; pt++)
970     {
971         BOOL bBad = TRUE;
972
973         switch (pt & (~MV_FLAG & PROP_TYPE_MASK))
974         {
975         case PT_UNSPECIFIED:
976         case PT_NULL: case PT_I2: case PT_I4: case PT_R4:
977         case PT_R8: case PT_CURRENCY: case PT_APPTIME:
978         case PT_ERROR: case PT_BOOLEAN: case PT_OBJECT:
979         case PT_I8: case PT_STRING8: case PT_UNICODE:
980         case PT_SYSTIME: case PT_CLSID: case PT_BINARY:
981             bBad = FALSE;
982         }
983
984         res = pFBadPropTag(pt);
985         if (bBad)
986             ok(res != 0, "pt= %ld: Expected non-zero, got 0\n", pt);
987         else
988             ok(res == 0, "pt= %ld: Expected zero, got %ld\n", pt, res);
989     }
990 }
991
992 static void test_FBadRow(void)
993 {
994     ULONG ulRet;
995
996     pFBadRow = (void*)GetProcAddress(hMapi32, "FBadRow@4");
997     if (!pFBadRow)
998         return;
999
1000     ulRet = pFBadRow(NULL);
1001     ok(ulRet != 0, "FBadRow(null): Expected non-zero, got 0\n");
1002
1003     /* FIXME */
1004 }
1005
1006 static void test_FBadProp(void)
1007 {
1008     static WCHAR szEmpty[] = { '\0' };
1009     GUID iid;
1010     ULONG pt, res;
1011     SPropValue pv;
1012
1013     pFBadProp = (void*)GetProcAddress(hMapi32, "FBadProp@4");
1014     if (!pFBadProp)
1015         return;
1016
1017     for (pt = 0; pt < PROP_ID_INVALID; pt++)
1018     {
1019         BOOL bBad = TRUE;
1020
1021         memset(&pv, 0, sizeof(pv));
1022         pv.ulPropTag = pt;
1023
1024         /* Note that MV values are valid below because their array count is 0,
1025          * so no pointers are validated.
1026          */
1027         switch (PROP_TYPE(pt))
1028         {
1029         case (MV_FLAG|PT_UNSPECIFIED):
1030         case PT_UNSPECIFIED:
1031         case (MV_FLAG|PT_NULL):
1032         case PT_NULL:
1033         case PT_MV_I2:
1034         case PT_I2:
1035         case PT_MV_I4:
1036         case PT_I4:
1037         case PT_MV_I8:
1038         case PT_I8:
1039         case PT_MV_R4:
1040         case PT_R4:
1041         case PT_MV_R8:
1042         case PT_R8:
1043         case PT_MV_CURRENCY:
1044         case PT_CURRENCY:
1045         case PT_MV_APPTIME:
1046         case PT_APPTIME:
1047         case (MV_FLAG|PT_ERROR):
1048         case PT_ERROR:
1049         case (MV_FLAG|PT_BOOLEAN):
1050         case PT_BOOLEAN:
1051         case (MV_FLAG|PT_OBJECT):
1052         case PT_OBJECT:
1053         case PT_MV_STRING8:
1054         case PT_MV_UNICODE:
1055         case PT_MV_SYSTIME:
1056         case PT_SYSTIME:
1057         case PT_MV_BINARY:
1058         case PT_BINARY:
1059         case PT_MV_CLSID:
1060             bBad = FALSE;
1061             break;
1062         case PT_STRING8:
1063         case PT_UNICODE:
1064             pv.Value.lpszW = szEmpty;
1065             bBad = FALSE;
1066             break;
1067         case PT_CLSID:
1068             pv.Value.lpguid = &iid;
1069             bBad = FALSE;
1070             break;
1071         }
1072
1073         res = pFBadProp(&pv);
1074         if (bBad)
1075             ok(res != 0, "pt= %ld: Expected non-zero, got 0\n", pt);
1076         else
1077             ok(res == 0, "pt= %ld: Expected zero, got %ld\n", pt, res);
1078     }
1079 }
1080
1081 static void test_FBadColumnSet(void)
1082 {
1083     SPropTagArray pta;
1084     ULONG pt, res;
1085
1086     pFBadColumnSet = (void*)GetProcAddress(hMapi32, "FBadColumnSet@4");
1087     if (!pFBadColumnSet)
1088         return;
1089
1090     res = pFBadColumnSet(NULL);
1091     ok(res != 0, "(null): Expected non-zero, got 0\n");
1092
1093     pta.cValues = 1;
1094
1095     for (pt = 0; pt < PROP_ID_INVALID; pt++)
1096     {
1097         BOOL bBad = TRUE;
1098
1099         pta.aulPropTag[0] = pt;
1100
1101         switch (pt & (~MV_FLAG & PROP_TYPE_MASK))
1102         {
1103         case PT_UNSPECIFIED:
1104         case PT_NULL:
1105         case PT_I2:
1106         case PT_I4:
1107         case PT_R4:
1108         case PT_R8:
1109         case PT_CURRENCY:
1110         case PT_APPTIME:
1111         case PT_BOOLEAN:
1112         case PT_OBJECT:
1113         case PT_I8:
1114         case PT_STRING8:
1115         case PT_UNICODE:
1116         case PT_SYSTIME:
1117         case PT_CLSID:
1118         case PT_BINARY:
1119             bBad = FALSE;
1120         }
1121         if (pt == (MV_FLAG|PT_ERROR))
1122             bBad = FALSE;
1123
1124         res = pFBadColumnSet(&pta);
1125         if (bBad)
1126             ok(res != 0, "pt= %ld: Expected non-zero, got 0\n", pt);
1127         else
1128             ok(res == 0, "pt= %ld: Expected zero, got %ld\n", pt, res);
1129     }
1130 }
1131
1132
1133 static void test_IProp(void)
1134 {
1135     IPropData *lpIProp;
1136     LPMAPIERROR lpError;
1137     LPSPropProblemArray lpProbs;
1138     LPSPropValue lpProps;
1139     LPSPropTagArray lpTags;
1140     SPropValue pvs[2];
1141     SizedSPropTagArray(2,tags);
1142     ULONG access[2], count;
1143     SCODE sc;
1144
1145     pCreateIProp = (void*)GetProcAddress(hMapi32, "CreateIProp@24");
1146
1147     if (!pCreateIProp)
1148         return;
1149
1150     memset(&tags, 0 , sizeof(tags));
1151
1152     /* Create the object */
1153     lpIProp = NULL;
1154     sc = pCreateIProp(&IID_IMAPIPropData, (ALLOCATEBUFFER *)pMAPIAllocateBuffer, (ALLOCATEMORE*)pMAPIAllocateMore,
1155                       (FREEBUFFER *)pMAPIFreeBuffer, NULL, &lpIProp);
1156     ok(sc == S_OK && lpIProp,
1157        "CreateIProp: expected S_OK, non-null, got 0x%08lX,%p\n", sc, lpIProp);
1158
1159     if (sc != S_OK || !lpIProp)
1160         return;
1161
1162     /* GetLastError - No errors set */
1163     lpError = NULL;
1164     IPropData_GetLastError(lpIProp, E_INVALIDARG, 0, &lpError);
1165     ok(sc == S_OK && !lpError,
1166        "GetLastError: Expected S_OK, null, got 0x%08lX,%p\n", sc, lpError);
1167
1168     /* Get prop tags - succeeds returning 0 items */
1169     lpTags = NULL;
1170     sc = IPropData_GetPropList(lpIProp, 0, &lpTags);
1171     ok(sc == S_OK && lpTags && lpTags->cValues == 0,
1172        "GetPropList(empty): Expected S_OK, non-null, 0, got 0x%08lX,%p,%ld\n",
1173         sc, lpTags, lpTags ? lpTags->cValues : 0);
1174     if (lpTags)
1175         pMAPIFreeBuffer(lpTags);
1176
1177     /* Get props - succeeds returning 0 items */
1178     lpProps = NULL;
1179     count = 0;
1180     tags.cValues = 1;
1181     tags.aulPropTag[0] = PR_IMPORTANCE;
1182     sc = IPropData_GetProps(lpIProp, (LPSPropTagArray)&tags, 0, &count, &lpProps);
1183     ok(sc == MAPI_W_ERRORS_RETURNED && lpProps && count == 1,
1184        "GetProps(empty): Expected ERRORS_RETURNED, non-null, 1, got 0x%08lX,%p,%ld\n",
1185        sc, lpProps, count);
1186     if (lpProps && count > 0)
1187     {
1188         ok(lpProps[0].ulPropTag == CHANGE_PROP_TYPE(PR_IMPORTANCE,PT_ERROR),
1189            "GetProps(empty): Expected %x, got %lx\n",
1190            CHANGE_PROP_TYPE(PR_IMPORTANCE,PT_ERROR), lpProps[0].ulPropTag);
1191
1192         pMAPIFreeBuffer(lpProps);
1193     }
1194
1195     /* Add (NULL) - Can't add NULL's */
1196     lpProbs = NULL;
1197     pvs[0].ulPropTag = PROP_TAG(PT_NULL,0x01);
1198     sc = IPropData_SetProps(lpIProp, 1, pvs, &lpProbs);
1199     ok(sc == MAPI_E_INVALID_PARAMETER && !lpProbs,
1200        "SetProps(): Expected INVALID_PARAMETER, null, got 0x%08lX,%p\n",
1201        sc, lpProbs);
1202
1203     /* Add (OBJECT) - Can't add OBJECTS's */
1204     lpProbs = NULL;
1205     pvs[0].ulPropTag = PROP_TAG(PT_OBJECT,0x01);
1206     sc = IPropData_SetProps(lpIProp, 1, pvs, &lpProbs);
1207     ok(sc == MAPI_E_INVALID_PARAMETER && !lpProbs,
1208        "SetProps(OBJECT): Expected INVALID_PARAMETER, null, got 0x%08lX,%p\n",
1209        sc, lpProbs);
1210
1211     /* Add - Adds value */
1212     lpProbs = NULL;
1213     pvs[0].ulPropTag = PR_IMPORTANCE;
1214     sc = IPropData_SetProps(lpIProp, 1, pvs, &lpProbs);
1215     ok(sc == S_OK && !lpProbs,
1216        "SetProps(ERROR): Expected S_OK, null, got 0x%08lX,%p\n", sc, lpProbs);
1217
1218     /* Get prop list - returns 1 item */
1219     lpTags = NULL;
1220     IPropData_GetPropList(lpIProp, 0, &lpTags);
1221     ok(sc == S_OK && lpTags && lpTags->cValues == 1,
1222        "GetPropList: Expected S_OK, non-null, 1, got 0x%08lX,%p,%ld\n",
1223         sc, lpTags, lpTags ? lpTags->cValues : 0);
1224     if (lpTags && lpTags->cValues > 0)
1225     {
1226         ok(lpTags->aulPropTag[0] == PR_IMPORTANCE,
1227            "GetPropList: Expected %x, got %lx\n",
1228            PR_IMPORTANCE, lpTags->aulPropTag[0]);
1229         pMAPIFreeBuffer(lpTags);
1230     }
1231
1232     /* Set access to read and write */
1233     sc = IPropData_HrSetObjAccess(lpIProp, IPROP_READWRITE);
1234     ok(sc == S_OK, "SetObjAcess(WRITE): Expected S_OK got 0x%08lX\n", sc);
1235
1236     tags.cValues = 1;
1237     tags.aulPropTag[0] = PR_IMPORTANCE;
1238
1239     /* Set item access (bad access) - Fails */
1240     access[0] = 0;
1241     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1242     ok(sc == MAPI_E_INVALID_PARAMETER,
1243        "SetPropAcess(0): Expected INVALID_PARAMETER got 0x%08lX\n",sc);
1244     access[0] = IPROP_READWRITE;
1245     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1246     ok(sc == MAPI_E_INVALID_PARAMETER,
1247        "SetPropAcess(RW): Expected INVALID_PARAMETER got 0x%08lX\n",sc);
1248     access[0] = IPROP_CLEAN;
1249     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1250     ok(sc == MAPI_E_INVALID_PARAMETER,
1251        "SetPropAcess(C): Expected INVALID_PARAMETER got 0x%08lX\n",sc);
1252
1253     /* Set item access to read/write/clean */
1254     tags.cValues = 1;
1255     tags.aulPropTag[0] = PR_IMPORTANCE;
1256     access[0] = IPROP_READWRITE|IPROP_CLEAN;
1257     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1258     ok(sc == S_OK, "SetPropAcess(RW/C): Expected S_OK got 0x%08lX\n",sc);
1259
1260     /* Set object access to read only */
1261     sc = IPropData_HrSetObjAccess(lpIProp, IPROP_READONLY);
1262     ok(sc == S_OK, "SetObjAcess(READ): Expected S_OK got 0x%08lX\n", sc);
1263
1264     /* Set item access to read/write/dirty - doesn't care about RO object */
1265     access[0] = IPROP_READONLY|IPROP_DIRTY;
1266     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1267     ok(sc == S_OK, "SetPropAcess(WRITE): Expected S_OK got 0x%08lX\n", sc);
1268
1269     /* Delete any item when set to read only - Error */
1270     lpProbs = NULL;
1271     tags.aulPropTag[0] = PR_RESPONSE_REQUESTED;
1272     sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
1273     ok(sc == E_ACCESSDENIED && !lpProbs,
1274        "DeleteProps(nonexistent): Expected E_ACCESSDENIED null got 0x%08lX %p\n",
1275        sc, lpProbs);
1276
1277     /* Set access to read and write */
1278     sc = IPropData_HrSetObjAccess(lpIProp, IPROP_READWRITE);
1279     ok(sc == S_OK, "SetObjAcess(WRITE): Expected S_OK got 0x%08lX\n", sc);
1280
1281     /* Delete nonexistent item - No error */
1282     lpProbs = NULL;
1283     tags.aulPropTag[0] = PR_RESPONSE_REQUESTED;
1284     sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
1285     ok(sc == S_OK && !lpProbs,
1286        "DeleteProps(nonexistent): Expected S_OK null got 0x%08lX %p\n",
1287        sc, lpProbs);
1288
1289     /* Delete existing item (r/o) - No error, but lpProbs populated */
1290     lpProbs = NULL;
1291     tags.aulPropTag[0] = PR_IMPORTANCE;
1292     sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
1293     ok(sc == S_OK && lpProbs,
1294        "DeleteProps(RO): Expected S_OK non-null got 0x%08lX %p\n", sc, lpProbs);
1295
1296     if (lpProbs && lpProbs->cProblem > 0)
1297     {
1298         ok(lpProbs->cProblem == 1 &&
1299            lpProbs->aProblem[0].ulIndex == 0 &&
1300            lpProbs->aProblem[0].ulPropTag == PR_IMPORTANCE &&
1301            lpProbs->aProblem[0].scode == E_ACCESSDENIED,
1302            "DeleteProps(RO): Expected (1,0,%x,%lx) got (%ld,%lx,%lx)\n",
1303             PR_IMPORTANCE, E_ACCESSDENIED,
1304             lpProbs->aProblem[0].ulIndex, lpProbs->aProblem[0].ulPropTag,
1305             lpProbs->aProblem[0].scode);
1306         pMAPIFreeBuffer(lpProbs);
1307     }
1308
1309     lpProbs = NULL;
1310     tags.cValues = 1;
1311     tags.aulPropTag[0] = PR_RESPONSE_REQUESTED;
1312     IPropData_HrAddObjProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
1313     ok(sc == S_OK && !lpProbs,
1314        "AddObjProps(RO): Expected S_OK null got 0x%08lX %p\n", sc, lpProbs);
1315
1316     /* Get prop list - returns 1 item */
1317     lpTags = NULL;
1318     IPropData_GetPropList(lpIProp, 0, &lpTags);
1319     ok(sc == S_OK && lpTags && lpTags->cValues == 1,
1320        "GetPropList: Expected S_OK, non-null, 1, got 0x%08lX,%p,%ld\n",
1321         sc, lpTags, lpTags ? lpTags->cValues : 0);
1322     if (lpTags && lpTags->cValues > 0)
1323     {
1324         ok(lpTags->aulPropTag[0] == PR_IMPORTANCE,
1325            "GetPropList: Expected %x, got %lx\n",
1326            PR_IMPORTANCE, lpTags->aulPropTag[0]);
1327         pMAPIFreeBuffer(lpTags);
1328     }
1329
1330     /* Set item to r/w again */
1331     access[0] = IPROP_READWRITE|IPROP_DIRTY;
1332     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1333     ok(sc == S_OK, "SetPropAcess(WRITE): Expected S_OK got 0x%08lX\n", sc);
1334
1335     /* Delete existing item (r/w) - No error, no problems */
1336     lpProbs = NULL;
1337     sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
1338     ok(sc == S_OK && !lpProbs,
1339        "DeleteProps(RO): Expected S_OK null got 0x%08lX %p\n", sc, lpProbs);
1340
1341     /* Free the list */
1342     IPropData_Release(lpIProp);
1343 }
1344
1345 START_TEST(prop)
1346 {
1347     if(!InitFuncPtrs())
1348         return;
1349
1350     pScInitMapiUtil(0);
1351
1352     test_PropCopyMore();
1353     test_UlPropSize();
1354     test_FPropContainsProp();
1355     test_FPropCompareProp();
1356     test_LPropCompareProp();
1357     test_PpropFindProp();
1358     test_ScCountProps();
1359     test_ScCopyRelocProps();
1360     test_LpValFindProp();
1361     test_FBadRglpszA();
1362     test_FBadRglpszW();
1363     test_FBadRowSet();
1364     test_FBadPropTag();
1365     test_FBadRow();
1366     test_FBadProp();
1367     test_FBadColumnSet();
1368
1369     test_IProp();
1370     FreeLibrary(hMapi32);
1371 }