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