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