Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/dlls/adsldp/tests/ldap.c
8812 views
1
/*
2
* LDAPNamespace Tests
3
*
4
* Copyright 2019 Dmitry Timoshkov
5
*
6
* This library is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU Lesser General Public
8
* License as published by the Free Software Foundation; either
9
* version 2.1 of the License, or (at your option) any later version.
10
*
11
* This library is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Lesser General Public License for more details.
15
*
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with this library; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19
*/
20
21
#include <stdarg.h>
22
#include <stdio.h>
23
24
#define COBJMACROS
25
26
#include "windef.h"
27
#include "winbase.h"
28
#include "objbase.h"
29
#include "iads.h"
30
#include "adserr.h"
31
#include "adshlp.h"
32
33
#include "wine/test.h"
34
35
#include "initguid.h"
36
DEFINE_GUID(CLSID_LDAP,0x228d9a81,0xc302,0x11cf,0x9a,0xa4,0x00,0xaa,0x00,0x4a,0x56,0x91);
37
DEFINE_GUID(CLSID_LDAPNamespace,0x228d9a82,0xc302,0x11cf,0x9a,0xa4,0x00,0xaa,0x00,0x4a,0x56,0x91);
38
DEFINE_OLEGUID(CLSID_PointerMoniker,0x306,0,0);
39
40
DEFINE_GUID(CLSID_Computers,0x252831aa,0x8876,0xd111,0xad,0xed,0x00,0xc0,0x4f,0xd8,0xd5,0xcd);
41
DEFINE_GUID(CLSID_Users,0x15cad1a9,0x8876,0xd111,0xad,0xed,0x00,0xc0,0x4f,0xd8,0xd5,0xcd);
42
43
static BOOL server_down;
44
45
static const struct
46
{
47
const WCHAR *path;
48
HRESULT hr, hr_ads_open, hr_ads_get;
49
const WCHAR *user, *password;
50
LONG flags;
51
} test[] =
52
{
53
{ L"invalid", MK_E_SYNTAX, E_ADS_BAD_PATHNAME, E_FAIL },
54
{ L"LDAP", MK_E_SYNTAX, E_ADS_BAD_PATHNAME, E_FAIL },
55
{ L"LDAP:", S_OK },
56
{ L"LDAP:/", E_ADS_BAD_PATHNAME },
57
{ L"LDAP://", E_ADS_BAD_PATHNAME },
58
{ L"LDAP://ldap.forumsys.com", S_OK },
59
{ L"LDAP:///ldap.forumsys.com", E_ADS_BAD_PATHNAME },
60
{ L"LDAP://ldap.forumsys.com:389", S_OK },
61
{ L"LDAP://ldap.forumsys.com:389/DC=example,DC=com", S_OK },
62
{ L"LDAP://ldap.forumsys.com/", E_ADS_BAD_PATHNAME },
63
{ L"LDAP://ldap.forumsys.com/rootDSE", S_OK },
64
{ L"LDAP://ldap.forumsys.com/rootDSE/", E_ADS_BAD_PATHNAME },
65
{ L"LDAP://ldap.forumsys.com/rootDSE/invalid", E_ADS_BAD_PATHNAME },
66
{ L"LDAP://ldap.forumsys.com/rootDSE", S_OK, S_OK, S_OK, NULL, NULL, 0 },
67
{ L"LDAP://ldap.forumsys.com/rootDSE", S_OK, S_OK, S_OK, L"CN=read-only-admin,DC=example,DC=com", L"password", 0 },
68
};
69
70
static void test_LDAP(void)
71
{
72
HRESULT hr;
73
IUnknown *unk;
74
IADs *ads;
75
IADsOpenDSObject *ads_open;
76
IDispatch *disp;
77
BSTR path, user, password;
78
int i;
79
80
if (server_down) return;
81
82
hr = CoCreateInstance(&CLSID_LDAPNamespace, 0, CLSCTX_INPROC_SERVER, &IID_IADs, (void **)&ads);
83
ok(hr == S_OK, "got %#lx\n", hr);
84
IADs_Release(ads);
85
86
hr = CoCreateInstance(&CLSID_LDAPNamespace, 0, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&unk);
87
ok(hr == S_OK, "got %#lx\n", hr);
88
89
hr = IUnknown_QueryInterface(unk, &IID_IDispatch, (void **)&disp);
90
ok(hr == S_OK, "got %#lx\n", hr);
91
IDispatch_Release(disp);
92
93
hr = IUnknown_QueryInterface(unk, &IID_IADsOpenDSObject, (void **)&ads_open);
94
ok(hr == S_OK, "got %#lx\n", hr);
95
96
for (i = 0; i < ARRAY_SIZE(test); i++)
97
{
98
path = SysAllocString(test[i].path);
99
user = test[i].user ? SysAllocString(test[i].user) : NULL;
100
password = test[i].password ? SysAllocString(test[i].password) : NULL;
101
102
hr = IADsOpenDSObject_OpenDSObject(ads_open, path, user, password, test[i].flags, &disp);
103
if (hr == HRESULT_FROM_WIN32(ERROR_DS_SERVER_DOWN))
104
{
105
SysFreeString(path);
106
skip("server is down\n");
107
server_down = TRUE;
108
break;
109
}
110
ok(hr == test[i].hr || hr == test[i].hr_ads_open, "%d: got %#lx, expected %#lx\n", i, hr, test[i].hr);
111
if (hr == S_OK)
112
IDispatch_Release(disp);
113
114
hr = ADsOpenObject(path, user, password, test[i].flags, &IID_IADs, (void **)&ads);
115
if (hr == HRESULT_FROM_WIN32(ERROR_DS_SERVER_DOWN))
116
{
117
SysFreeString(path);
118
skip("server is down\n");
119
server_down = TRUE;
120
break;
121
}
122
ok(hr == test[i].hr || hr == test[i].hr_ads_get, "%d: got %#lx, expected %#lx\n", i, hr, test[i].hr);
123
if (hr == S_OK)
124
IADs_Release(ads);
125
126
hr = ADsGetObject(path, &IID_IDispatch, (void **)&disp);
127
if (hr == HRESULT_FROM_WIN32(ERROR_DS_SERVER_DOWN))
128
{
129
SysFreeString(path);
130
skip("server is down\n");
131
server_down = TRUE;
132
break;
133
}
134
ok(hr == test[i].hr || hr == test[i].hr_ads_get, "%d: got %#lx, expected %#lx\n", i, hr, test[i].hr);
135
if (hr == S_OK)
136
IDispatch_Release(disp);
137
138
SysFreeString(path);
139
SysFreeString(user);
140
SysFreeString(password);
141
}
142
143
144
IADsOpenDSObject_Release(ads_open);
145
IUnknown_Release(unk);
146
}
147
148
static void test_ParseDisplayName(void)
149
{
150
HRESULT hr;
151
IBindCtx *bc;
152
IParseDisplayName *parse;
153
IMoniker *mk;
154
IUnknown *unk;
155
CLSID clsid;
156
BSTR path;
157
ULONG count;
158
int i;
159
160
if (server_down) return;
161
162
hr = CoCreateInstance(&CLSID_LDAP, 0, CLSCTX_INPROC_SERVER, &IID_IParseDisplayName, (void **)&parse);
163
ok(hr == S_OK, "got %#lx\n", hr);
164
IParseDisplayName_Release(parse);
165
166
hr = CoCreateInstance(&CLSID_LDAP, 0, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&unk);
167
ok(hr == S_OK, "got %#lx\n", hr);
168
hr = IUnknown_QueryInterface(unk, &IID_IParseDisplayName, (void **)&parse);
169
ok(hr == S_OK, "got %#lx\n", hr);
170
IUnknown_Release(unk);
171
172
hr = CreateBindCtx(0, &bc);
173
ok(hr == S_OK, "got %#lx\n", hr);
174
175
for (i = 0; i < ARRAY_SIZE(test); i++)
176
{
177
path = SysAllocString(test[i].path);
178
179
count = 0xdeadbeef;
180
hr = IParseDisplayName_ParseDisplayName(parse, bc, path, &count, &mk);
181
if (hr == HRESULT_FROM_WIN32(ERROR_DS_SERVER_DOWN))
182
{
183
SysFreeString(path);
184
skip("server is down\n");
185
server_down = TRUE;
186
break;
187
}
188
ok(hr == test[i].hr || hr == test[i].hr_ads_open, "%d: got %#lx, expected %#lx\n", i, hr, test[i].hr);
189
if (hr == S_OK)
190
{
191
ok(count == lstrlenW(test[i].path), "%d: got %ld\n", i, count);
192
193
hr = IMoniker_GetClassID(mk, &clsid);
194
ok(hr == S_OK, "got %#lx\n", hr);
195
ok(IsEqualGUID(&clsid, &CLSID_PointerMoniker), "%d: got %s\n", i, wine_dbgstr_guid(&clsid));
196
197
IMoniker_Release(mk);
198
}
199
200
SysFreeString(path);
201
202
count = 0xdeadbeef;
203
hr = MkParseDisplayName(bc, test[i].path, &count, &mk);
204
if (hr == HRESULT_FROM_WIN32(ERROR_DS_SERVER_DOWN))
205
{
206
skip("server is down\n");
207
server_down = TRUE;
208
break;
209
}
210
todo_wine_if(i == 0 || i == 1)
211
ok(hr == test[i].hr, "%d: got %#lx, expected %#lx\n", i, hr, test[i].hr);
212
if (hr == S_OK)
213
{
214
ok(count == lstrlenW(test[i].path), "%d: got %ld\n", i, count);
215
216
hr = IMoniker_GetClassID(mk, &clsid);
217
ok(hr == S_OK, "got %#lx\n", hr);
218
ok(IsEqualGUID(&clsid, &CLSID_PointerMoniker), "%d: got %s\n", i, wine_dbgstr_guid(&clsid));
219
220
IMoniker_Release(mk);
221
}
222
}
223
224
IBindCtx_Release(bc);
225
IParseDisplayName_Release(parse);
226
}
227
228
struct result
229
{
230
const WCHAR *name;
231
ADSTYPEENUM type;
232
const WCHAR *values[16];
233
};
234
235
struct search
236
{
237
const WCHAR *dn;
238
ADS_SCOPEENUM scope;
239
struct result res[16];
240
};
241
242
static void do_search(const struct search *s)
243
{
244
HRESULT hr;
245
IDirectorySearch *ds;
246
ADS_SEARCHPREF_INFO pref[2];
247
ADS_SEARCH_HANDLE sh;
248
ADS_SEARCH_COLUMN col;
249
LPWSTR name;
250
const struct result *res;
251
252
if (server_down) return;
253
254
trace("search DN %s\n", wine_dbgstr_w(s->dn));
255
256
hr = ADsGetObject(s->dn, &IID_IDirectorySearch, (void **)&ds);
257
if (hr == HRESULT_FROM_WIN32(ERROR_DS_SERVER_DOWN))
258
{
259
skip("server is down\n");
260
server_down = TRUE;
261
return;
262
}
263
ok(hr == S_OK, "got %#lx\n", hr);
264
265
pref[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
266
pref[0].vValue.dwType = ADSTYPE_INTEGER;
267
pref[0].vValue.Integer = s->scope;
268
pref[0].dwStatus = 0xdeadbeef;
269
270
pref[1].dwSearchPref = ADS_SEARCHPREF_SIZE_LIMIT;
271
pref[1].vValue.dwType = ADSTYPE_INTEGER;
272
pref[1].vValue.Integer = 5;
273
pref[1].dwStatus = 0xdeadbeef;
274
275
hr = IDirectorySearch_SetSearchPreference(ds, pref, ARRAY_SIZE(pref));
276
ok(hr == S_OK, "got %#lx\n", hr);
277
ok(pref[0].dwStatus == ADS_STATUS_S_OK, "got %u\n", pref[0].dwStatus);
278
ok(pref[1].dwStatus == ADS_STATUS_S_OK, "got %u\n", pref[1].dwStatus);
279
280
hr = IDirectorySearch_ExecuteSearch(ds, (WCHAR *)L"(objectClass=*)", NULL, ~0, &sh);
281
ok(hr == S_OK, "got %#lx\n", hr);
282
283
res = s->res;
284
285
while ((hr = IDirectorySearch_GetNextRow(ds, sh)) != S_ADS_NOMORE_ROWS)
286
{
287
ok(hr == S_OK, "got %#lx\n", hr);
288
289
while ((hr = IDirectorySearch_GetNextColumnName(ds, sh, &name)) != S_ADS_NOMORE_COLUMNS)
290
{
291
DWORD i;
292
293
ok(hr == S_OK, "got %#lx\n", hr);
294
ok(res->name != NULL, "got extra row %s\n", wine_dbgstr_w(name));
295
ok(!wcscmp(res->name, name), "expected %s, got %s\n", wine_dbgstr_w(res->name), wine_dbgstr_w(name));
296
297
memset(&col, 0xde, sizeof(col));
298
hr = IDirectorySearch_GetColumn(ds, sh, name, &col);
299
ok(hr == S_OK, "got %#lx\n", hr);
300
301
ok(col.dwADsType == res->type, "got %d for %s\n", col.dwADsType, wine_dbgstr_w(name));
302
303
for (i = 0; i < col.dwNumValues; i++)
304
{
305
ok(col.pADsValues[i].dwType == col.dwADsType, "%lu: got %d for %s\n", i, col.pADsValues[i].dwType, wine_dbgstr_w(name));
306
307
ok(res->values[i] != NULL, "expected to have more values for %s\n", wine_dbgstr_w(name));
308
if (!res->values[i]) break;
309
310
ok(!wcscmp(res->values[i], col.pADsValues[i].CaseIgnoreString),
311
"expected %s, got %s\n", wine_dbgstr_w(res->values[i]), wine_dbgstr_w(col.pADsValues[i].CaseIgnoreString));
312
}
313
314
IDirectorySearch_FreeColumn(ds, &col);
315
FreeADsMem(name);
316
res++;
317
}
318
}
319
320
ok(res->name == NULL, "there are more rows in test data: %s\n", wine_dbgstr_w(res->name));
321
322
hr = IDirectorySearch_CloseSearchHandle(ds, sh);
323
ok(hr == S_OK, "got %#lx\n", hr);
324
325
IDirectorySearch_Release(ds);
326
}
327
328
static void test_DirectorySearch(void)
329
{
330
static const struct search root_base =
331
{
332
L"LDAP://ldap.forumsys.com", ADS_SCOPE_BASE,
333
{
334
{ L"objectClass", ADSTYPE_CASE_IGNORE_STRING, { L"top", L"OpenLDAProotDSE", NULL } },
335
{ L"ADsPath", ADSTYPE_CASE_IGNORE_STRING, { L"LDAP://ldap.forumsys.com/", NULL } },
336
{ NULL }
337
}
338
};
339
static const struct search scientists_base =
340
{
341
L"LDAP://ldap.forumsys.com/OU=scientists,DC=example,DC=com", ADS_SCOPE_BASE,
342
{
343
{ L"uniqueMember", ADSTYPE_CASE_IGNORE_STRING, { L"uid=einstein,dc=example,dc=com",
344
L"uid=tesla,dc=example,dc=com", L"uid=newton,dc=example,dc=com", L"uid=galileo,dc=example,dc=com",
345
L"uid=training,dc=example,dc=com", L"uid=jmacy,dc=example,dc=com", NULL } },
346
{ L"ou", ADSTYPE_CASE_IGNORE_STRING, { L"scientists", NULL } },
347
{ L"cn", ADSTYPE_CASE_IGNORE_STRING, { L"Scientists", NULL } },
348
{ L"objectClass", ADSTYPE_CASE_IGNORE_STRING, { L"groupOfUniqueNames", L"top", NULL } },
349
{ L"ADsPath", ADSTYPE_CASE_IGNORE_STRING, { L"LDAP://ldap.forumsys.com/ou=scientists,dc=example,dc=com", NULL } },
350
{ NULL }
351
}
352
};
353
static const struct search scientists_subtree =
354
{
355
L"LDAP://ldap.forumsys.com/OU=scientists,DC=example,DC=com", ADS_SCOPE_SUBTREE,
356
{
357
{ L"uniqueMember", ADSTYPE_CASE_IGNORE_STRING, { L"uid=einstein,dc=example,dc=com",
358
L"uid=tesla,dc=example,dc=com", L"uid=newton,dc=example,dc=com", L"uid=galileo,dc=example,dc=com",
359
L"uid=training,dc=example,dc=com", L"uid=jmacy,dc=example,dc=com", NULL } },
360
{ L"ou", ADSTYPE_CASE_IGNORE_STRING, { L"scientists", NULL } },
361
{ L"cn", ADSTYPE_CASE_IGNORE_STRING, { L"Scientists", NULL } },
362
{ L"objectClass", ADSTYPE_CASE_IGNORE_STRING, { L"groupOfUniqueNames", L"top", NULL } },
363
{ L"ADsPath", ADSTYPE_CASE_IGNORE_STRING, { L"LDAP://ldap.forumsys.com/ou=scientists,dc=example,dc=com", NULL } },
364
{ L"uniqueMember", ADSTYPE_CASE_IGNORE_STRING, { L"uid=tesla,dc=example,dc=com", NULL } },
365
{ L"ou", ADSTYPE_CASE_IGNORE_STRING, { L"italians", NULL } },
366
{ L"cn", ADSTYPE_CASE_IGNORE_STRING, { L"Italians", NULL } },
367
{ L"objectClass", ADSTYPE_CASE_IGNORE_STRING, { L"groupOfUniqueNames", L"top", NULL } },
368
{ L"ADsPath", ADSTYPE_CASE_IGNORE_STRING, { L"LDAP://ldap.forumsys.com/ou=italians,ou=scientists,dc=example,dc=com", NULL } },
369
{ NULL }
370
}
371
};
372
HRESULT hr;
373
IDirectorySearch *ds;
374
ADS_SEARCHPREF_INFO pref[2];
375
ADS_SEARCH_HANDLE sh;
376
ADS_SEARCH_COLUMN col;
377
LPWSTR name;
378
379
if (server_down) return;
380
381
hr = ADsGetObject(L"LDAP:", &IID_IDirectorySearch, (void **)&ds);
382
ok(hr == E_NOINTERFACE, "got %#lx\n", hr);
383
384
hr = ADsGetObject(L"LDAP://ldap.forumsys.com/rootDSE", &IID_IDirectorySearch, (void **)&ds);
385
if (hr == HRESULT_FROM_WIN32(ERROR_DS_SERVER_DOWN))
386
{
387
skip("server is down\n");
388
server_down = TRUE;
389
return;
390
}
391
ok(hr == E_NOINTERFACE, "got %#lx\n", hr);
392
393
hr = ADsGetObject(L"LDAP://ldap.forumsys.com", &IID_IDirectorySearch, (void **)&ds);
394
if (hr == HRESULT_FROM_WIN32(ERROR_DS_SERVER_DOWN))
395
{
396
skip("server is down\n");
397
server_down = TRUE;
398
return;
399
}
400
ok(hr == S_OK, "got %#lx\n", hr);
401
402
pref[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
403
pref[0].vValue.dwType = ADSTYPE_INTEGER;
404
pref[0].vValue.Integer = ADS_SCOPE_BASE;
405
pref[0].dwStatus = 0xdeadbeef;
406
407
pref[1].dwSearchPref = ADS_SEARCHPREF_SIZE_LIMIT;
408
pref[1].vValue.dwType = ADSTYPE_INTEGER;
409
pref[1].vValue.Integer = 5;
410
pref[1].dwStatus = 0xdeadbeef;
411
412
hr = IDirectorySearch_SetSearchPreference(ds, pref, ARRAY_SIZE(pref));
413
ok(hr == S_OK, "got %#lx\n", hr);
414
ok(pref[0].dwStatus == ADS_STATUS_S_OK, "got %u\n", pref[0].dwStatus);
415
ok(pref[1].dwStatus == ADS_STATUS_S_OK, "got %u\n", pref[1].dwStatus);
416
417
hr = IDirectorySearch_ExecuteSearch(ds, (WCHAR *)L"(objectClass=*)", NULL, ~0, NULL);
418
ok(hr == E_ADS_BAD_PARAMETER, "got %#lx\n", hr);
419
420
hr = IDirectorySearch_ExecuteSearch(ds, (WCHAR *)L"(objectClass=*)", NULL, 1, &sh);
421
ok(hr == E_ADS_BAD_PARAMETER, "got %#lx\n", hr);
422
423
hr = IDirectorySearch_ExecuteSearch(ds, (WCHAR *)L"(objectClass=*)", NULL, ~0, &sh);
424
ok(hr == S_OK, "got %#lx\n", hr);
425
426
hr = IDirectorySearch_GetNextColumnName(ds, sh, &name);
427
ok(hr == E_ADS_BAD_PARAMETER, "got %#lx\n", hr);
428
429
hr = IDirectorySearch_GetPreviousRow(ds, sh);
430
todo_wine
431
ok(hr == E_ADS_BAD_PARAMETER, "got %#lx\n", hr);
432
433
while (IDirectorySearch_GetNextRow(ds, sh) != S_ADS_NOMORE_ROWS)
434
{
435
while (IDirectorySearch_GetNextColumnName(ds, sh, &name) != S_ADS_NOMORE_COLUMNS)
436
{
437
DWORD i;
438
439
hr = IDirectorySearch_GetColumn(ds, sh, name, &col);
440
ok(hr == S_OK, "got %#lx for column %s\n", hr, wine_dbgstr_w(name));
441
442
if (winetest_debug > 1) /* useful to create test arrays */
443
{
444
printf("Column %s (values type %d):\n", wine_dbgstr_w(name), col.dwADsType);
445
printf("{ ");
446
for (i = 0; i < col.dwNumValues; i++)
447
printf("%s, ", wine_dbgstr_w(col.pADsValues[i].CaseIgnoreString));
448
printf("NULL }\n");
449
}
450
451
hr = IDirectorySearch_FreeColumn(ds, &col);
452
ok(hr == S_OK, "got %#lx\n", hr);
453
FreeADsMem(name);
454
}
455
456
name = (void *)0xdeadbeef;
457
hr = IDirectorySearch_GetNextColumnName(ds, sh, &name);
458
ok(hr == S_ADS_NOMORE_COLUMNS, "got %#lx\n", hr);
459
ok(name == NULL, "got %p/%s\n", name, wine_dbgstr_w(name));
460
}
461
462
hr = IDirectorySearch_GetNextRow(ds, sh);
463
ok(hr == S_ADS_NOMORE_ROWS, "got %#lx\n", hr);
464
465
name = NULL;
466
hr = IDirectorySearch_GetNextColumnName(ds, sh, &name);
467
todo_wine
468
ok(hr == S_OK, "got %#lx\n", hr);
469
todo_wine
470
ok(name && !wcscmp(name, L"ADsPath"), "got %s\n", wine_dbgstr_w(name));
471
FreeADsMem(name);
472
473
name = (void *)0xdeadbeef;
474
hr = IDirectorySearch_GetNextColumnName(ds, sh, &name);
475
ok(hr == S_ADS_NOMORE_COLUMNS, "got %#lx\n", hr);
476
ok(name == NULL, "got %p/%s\n", name, wine_dbgstr_w(name));
477
478
hr = IDirectorySearch_GetColumn(ds, sh, NULL, &col);
479
ok(hr == E_ADS_BAD_PARAMETER, "got %#lx\n", hr);
480
481
hr = IDirectorySearch_GetFirstRow(ds, sh);
482
ok(hr == S_OK, "got %#lx\n", hr);
483
484
memset(&col, 0x55, sizeof(col));
485
hr = IDirectorySearch_GetColumn(ds, sh, (WCHAR *)L"deadbeef", &col);
486
ok(hr == E_ADS_COLUMN_NOT_SET, "got %#lx\n", hr);
487
ok(!col.pszAttrName, "got %p\n", col.pszAttrName);
488
ok(col.dwADsType == ADSTYPE_INVALID, "got %d\n", col.dwADsType);
489
ok(!col.pADsValues, "got %p\n", col.pADsValues);
490
ok(!col.dwNumValues, "got %lu\n", col.dwNumValues);
491
ok(!col.hReserved, "got %p\n", col.hReserved);
492
493
hr = IDirectorySearch_CloseSearchHandle(ds, sh);
494
ok(hr == S_OK, "got %#lx\n", hr);
495
496
IDirectorySearch_Release(ds);
497
498
do_search(&root_base);
499
do_search(&scientists_base);
500
do_search(&scientists_subtree);
501
}
502
503
static void test_DirectoryObject(void)
504
{
505
HRESULT hr;
506
IDirectoryObject *dirobj;
507
IUnknown *unk;
508
IDirectorySearch *ds;
509
ADS_SEARCHPREF_INFO pref[3];
510
ADS_SEARCH_HANDLE sh;
511
ADS_SEARCH_COLUMN col;
512
513
if (server_down) return;
514
515
hr = ADsGetObject(L"LDAP://ldap.forumsys.com/OU=scientists,DC=example,DC=com", &IID_IDirectoryObject, (void **)&dirobj);
516
if (hr == HRESULT_FROM_WIN32(ERROR_DS_SERVER_DOWN))
517
{
518
skip("server is down\n");
519
server_down = TRUE;
520
return;
521
}
522
ok(hr == S_OK, "got %#lx\n", hr);
523
524
hr = IDirectoryObject_QueryInterface(dirobj, &IID_IADsOpenDSObject, (void **)&unk);
525
todo_wine
526
ok(hr == E_NOINTERFACE, "got %#lx\n", hr);
527
if (hr == S_OK) IUnknown_Release(unk);
528
hr = IDirectoryObject_QueryInterface(dirobj, &IID_IDispatch, (void **)&unk);
529
ok(hr == S_OK, "got %#lx\n", hr);
530
IUnknown_Release(unk);
531
hr = IDirectoryObject_QueryInterface(dirobj, &IID_IADs, (void **)&unk);
532
ok(hr == S_OK, "got %#lx\n", hr);
533
IUnknown_Release(unk);
534
hr = IDirectoryObject_QueryInterface(dirobj, &IID_IDirectorySearch, (void **)&ds);
535
ok(hr == S_OK, "got %#lx\n", hr);
536
537
pref[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
538
pref[0].vValue.dwType = ADSTYPE_INTEGER;
539
pref[0].vValue.Integer = ADS_SCOPE_BASE;
540
pref[0].dwStatus = 0xdeadbeef;
541
542
pref[1].dwSearchPref = ADS_SEARCHPREF_SECURITY_MASK;
543
pref[1].vValue.dwType = ADSTYPE_INTEGER;
544
pref[1].vValue.Integer = ADS_SECURITY_INFO_OWNER | ADS_SECURITY_INFO_GROUP | ADS_SECURITY_INFO_DACL;
545
pref[1].dwStatus = 0xdeadbeef;
546
547
pref[2].dwSearchPref = ADS_SEARCHPREF_SIZE_LIMIT;
548
pref[2].vValue.dwType = ADSTYPE_INTEGER;
549
pref[2].vValue.Integer = 5;
550
pref[2].dwStatus = 0xdeadbeef;
551
552
hr = IDirectorySearch_SetSearchPreference(ds, pref, ARRAY_SIZE(pref));
553
ok(hr == S_ADS_ERRORSOCCURRED, "got %#lx\n", hr);
554
ok(pref[0].dwStatus == ADS_STATUS_S_OK, "got %d\n", pref[0].dwStatus);
555
/* ldap.forumsys.com doesn't support NT security, real ADs DC - does */
556
ok(pref[1].dwStatus == ADS_STATUS_INVALID_SEARCHPREF, "got %d\n", pref[1].dwStatus);
557
ok(pref[2].dwStatus == ADS_STATUS_S_OK, "got %d\n", pref[2].dwStatus);
558
559
hr = IDirectorySearch_ExecuteSearch(ds, (WCHAR *)L"(objectClass=*)", NULL, ~0, &sh);
560
ok(hr == S_OK, "got %#lx\n", hr);
561
562
hr = IDirectorySearch_GetNextRow(ds, sh);
563
ok(hr == S_OK, "got %#lx\n", hr);
564
565
hr = IDirectorySearch_GetColumn(ds, sh, (WCHAR *)L"nTSecurityDescriptor", &col);
566
ok(hr == E_ADS_COLUMN_NOT_SET, "got %#lx\n", hr);
567
568
hr = IDirectorySearch_CloseSearchHandle(ds, sh);
569
ok(hr == S_OK, "got %#lx\n", hr);
570
571
pref[0].dwSearchPref = ADS_SEARCHPREF_TOMBSTONE;
572
pref[0].vValue.dwType = ADSTYPE_BOOLEAN;
573
pref[0].vValue.Integer = 1;
574
pref[0].dwStatus = 0xdeadbeef;
575
576
pref[1].dwSearchPref = ADS_SEARCHPREF_SIZE_LIMIT;
577
pref[1].vValue.dwType = ADSTYPE_INTEGER;
578
pref[1].vValue.Integer = 5;
579
pref[1].dwStatus = 0xdeadbeef;
580
581
hr = IDirectorySearch_SetSearchPreference(ds, pref, 2);
582
ok(hr == S_OK, "got %#lx\n", hr);
583
ok(pref[0].dwStatus == ADS_STATUS_S_OK, "got %d\n", pref[0].dwStatus);
584
ok(pref[1].dwStatus == ADS_STATUS_S_OK, "got %d\n", pref[1].dwStatus);
585
586
hr = IDirectorySearch_ExecuteSearch(ds, (WCHAR *)L"(objectClass=*)", NULL, ~0, &sh);
587
todo_wine ok(hr == S_OK, "got %#lx\n", hr);
588
if (hr == S_OK)
589
{
590
hr = IDirectorySearch_GetNextRow(ds, sh);
591
ok(hr == HRESULT_FROM_WIN32(ERROR_DS_UNAVAILABLE_CRIT_EXTENSION), "got %#lx\n", hr);
592
593
hr = IDirectorySearch_CloseSearchHandle(ds, sh);
594
ok(hr == S_OK, "got %#lx\n", hr);
595
}
596
597
IDirectorySearch_Release(ds);
598
IDirectoryObject_Release(dirobj);
599
}
600
601
static void get_attribute(IADs *ads, BSTR name, int vt)
602
{
603
HRESULT hr;
604
VARIANT var;
605
606
VariantInit(&var);
607
hr = IADs_GetEx(ads, name, &var);
608
ok(hr == S_OK, "GetEx(%s) error %#lx\n", wine_dbgstr_w(name), hr);
609
ok(V_VT(&var) == vt, "attribute %s has vt %#x\n", wine_dbgstr_w(name), V_VT(&var));
610
if (V_VT(&var) != vt) return;
611
612
if (V_VT(&var) | VT_ARRAY)
613
{
614
VARIANT item;
615
LONG start, end, idx;
616
617
hr = SafeArrayGetLBound(V_ARRAY(&var), 1, &start);
618
ok(hr == S_OK, "got %#lx\n", hr);
619
hr = SafeArrayGetUBound(V_ARRAY(&var), 1, &end);
620
ok(hr == S_OK, "got %#lx\n", hr);
621
for (idx = start; idx <= end; idx++)
622
{
623
VariantInit(&item);
624
hr = SafeArrayGetElement(V_ARRAY(&var), &idx, &item);
625
ok(hr == S_OK, "got %#lx\n", hr);
626
ok(V_VT(&item) == VT_BSTR, "%ld: got %d\n", idx, V_VT(&item));
627
trace("%s[%ld]: %s\n", wine_dbgstr_w(name), idx, wine_dbgstr_w(V_BSTR(&item)));
628
VariantClear(&item);
629
}
630
}
631
VariantClear(&var);
632
}
633
634
static void test_IADs_GetEx(void)
635
{
636
HRESULT hr;
637
IADs *ads, *ads2;
638
BSTR bstr;
639
VARIANT var, item, bin;
640
LONG start, end, idx, found;
641
WCHAR path[256];
642
IADsDNWithBinary *dn;
643
644
hr = ADsGetObject(L"LDAP://rootDSE", &IID_IADs, (void **)&ads);
645
if (hr != S_OK)
646
{
647
skip("Computer is not part of an Active Directory Domain\n");
648
return;
649
}
650
651
hr = IADs_get_Schema(ads, &bstr);
652
ok(hr == E_ADS_PROPERTY_NOT_SUPPORTED, "got %#lx\n", hr);
653
654
VariantInit(&var);
655
hr = IADs_Get(ads, (BSTR)L"objectClass", &var);
656
ok(hr == E_ADS_PROPERTY_NOT_FOUND, "got %#lx\n", hr);
657
658
VariantInit(&var);
659
hr = IADs_Get(ads, (BSTR)L"defaultNamingContext", &var);
660
ok(hr == S_OK, "got %#lx\n", hr);
661
ok(V_VT(&var) == VT_BSTR, "got %#x\n", V_VT(&var));
662
trace("defaultNamingContext: %s\n", wine_dbgstr_w(V_BSTR(&var)));
663
wcscpy(path, L"LDAP://");
664
wcscat(path, V_BSTR(&var));
665
VariantClear(&var);
666
667
get_attribute(ads, (BSTR)L"defaultNamingContext", VT_ARRAY | VT_VARIANT);
668
669
VariantInit(&var);
670
hr = IADs_Get(ads, (BSTR)L"wellKnownObjects", &var);
671
ok(hr == E_ADS_PROPERTY_NOT_FOUND, "got %#lx\n", hr);
672
673
IADs_Release(ads);
674
675
hr = ADsGetObject(path, &IID_IADs, (void **)&ads);
676
ok(hr == S_OK, "got %#lx\n", hr);
677
678
get_attribute(ads, (BSTR)L"objectClass", VT_ARRAY | VT_VARIANT);
679
680
hr = IADs_get_Schema(ads, &bstr);
681
ok(hr == S_OK, "got %#lx\n", hr);
682
trace("Schema of %s: %s\n", wine_dbgstr_w(path), wine_dbgstr_w(bstr));
683
SysFreeString(bstr);
684
685
/* ::Get() */
686
VariantInit(&var);
687
hr = IADs_Get(ads, (BSTR)L"wellKnownObjects", &var);
688
ok(hr == S_OK, "got %#lx\n", hr);
689
ok(V_VT(&var) == (VT_ARRAY | VT_VARIANT), "got %#x\n", V_VT(&var));
690
hr = SafeArrayGetLBound(V_ARRAY(&var), 1, &start);
691
ok(hr == S_OK, "got %#lx\n", hr);
692
ok(start == 0, "got start %ld\n", start);
693
hr = SafeArrayGetUBound(V_ARRAY(&var), 1, &end);
694
ok(hr == S_OK, "got %#lx\n", hr);
695
trace("wellKnownObjects contains %lu elements\n", end - start + 1);
696
VariantInit(&item);
697
found = 0;
698
for (idx = start; idx <= end; idx++)
699
{
700
hr = SafeArrayGetElement(V_ARRAY(&var), &idx, &item);
701
ok(hr == S_OK, "got %#lx\n", hr);
702
ok(V_VT(&item) == VT_DISPATCH, "%ld: got %d\n", idx, V_VT(&item));
703
hr = IDispatch_QueryInterface(V_DISPATCH(&item), &IID_IADsDNWithBinary, (void **)&dn);
704
ok(hr == S_OK, "IID_IADsDNWithBinary: got %#lx\n", hr);
705
706
hr = IADsDNWithBinary_get_DNString(dn, &bstr);
707
ok(hr == S_OK, "got %#lx\n", hr);
708
trace("%ld: DNString => %s\n", idx, wine_dbgstr_w(bstr));
709
710
VariantInit(&bin);
711
hr = IADsDNWithBinary_get_BinaryValue(dn, &bin);
712
ok(hr == S_OK, "got %#lx\n", hr);
713
ok(V_VT(&bin) == (VT_ARRAY | VT_UI1), "%ld: got %d\n", idx, V_VT(&bin));
714
ok(V_ARRAY(&bin)->cDims == 1, "%ld: got cDims %u\n", idx, V_ARRAY(&bin)->cDims);
715
ok(V_ARRAY(&bin)->cbElements == 1, "%ld: got cDims %lu\n", idx, V_ARRAY(&bin)->cbElements);
716
ok(V_ARRAY(&bin)->rgsabound[0].cElements == sizeof(CLSID),
717
"%ld: got cElements %lu\n", idx, V_ARRAY(&bin)->rgsabound[0].cElements);
718
719
if(!memcmp(V_ARRAY(&bin)->pvData, &CLSID_Computers, sizeof(CLSID)) ||
720
!memcmp(V_ARRAY(&bin)->pvData, &CLSID_Users, sizeof(CLSID)))
721
found++;
722
723
IADsDNWithBinary_Release(dn);
724
VariantClear(&item);
725
}
726
VariantClear(&var);
727
ok(found == 2, "Users or Computers entry not found\n");
728
729
/* ::GetEx() */
730
VariantInit(&var);
731
hr = IADs_GetEx(ads, (BSTR)L"wellKnownObjects", &var);
732
ok(hr == S_OK, "got %#lx\n", hr);
733
ok(V_VT(&var) == (VT_ARRAY | VT_VARIANT), "got %#x\n", V_VT(&var));
734
hr = SafeArrayGetLBound(V_ARRAY(&var), 1, &start);
735
ok(hr == S_OK, "got %#lx\n", hr);
736
ok(start == 0, "got start %ld\n", start);
737
hr = SafeArrayGetUBound(V_ARRAY(&var), 1, &end);
738
ok(hr == S_OK, "got %#lx\n", hr);
739
VariantInit(&item);
740
found = 0;
741
for (idx = start; idx <= end; idx++)
742
{
743
hr = SafeArrayGetElement(V_ARRAY(&var), &idx, &item);
744
ok(hr == S_OK, "got %#lx\n", hr);
745
ok(V_VT(&item) == VT_DISPATCH, "%ld: got %d\n", idx, V_VT(&item));
746
hr = IDispatch_QueryInterface(V_DISPATCH(&item), &IID_IADsDNWithBinary, (void **)&dn);
747
ok(hr == S_OK, "IID_IADsDNWithBinary: got %#lx\n", hr);
748
749
hr = IADsDNWithBinary_get_DNString(dn, &bstr);
750
ok(hr == S_OK, "got %#lx\n", hr);
751
trace("%ld: DNString => %s\n", idx, wine_dbgstr_w(bstr));
752
753
VariantInit(&bin);
754
hr = IADsDNWithBinary_get_BinaryValue(dn, &bin);
755
ok(hr == S_OK, "got %#lx\n", hr);
756
ok(V_VT(&bin) == (VT_ARRAY | VT_UI1), "%ld: got %d\n", idx, V_VT(&bin));
757
ok(V_ARRAY(&bin)->cDims == 1, "%ld: got cDims %u\n", idx, V_ARRAY(&bin)->cDims);
758
ok(V_ARRAY(&bin)->cbElements == 1, "%ld: got cDims %lu\n", idx, V_ARRAY(&bin)->cbElements);
759
ok(V_ARRAY(&bin)->rgsabound[0].cElements == sizeof(CLSID),
760
"%ld: got cElements %lu\n", idx, V_ARRAY(&bin)->rgsabound[0].cElements);
761
762
if(!memcmp(V_ARRAY(&bin)->pvData, &CLSID_Computers, sizeof(CLSID)) ||
763
!memcmp(V_ARRAY(&bin)->pvData, &CLSID_Users, sizeof(CLSID)))
764
found++;
765
766
wcscpy(path, L"LDAP://");
767
wcscat(path, bstr);
768
SysFreeString(bstr);
769
hr = ADsOpenObject(path, NULL, NULL, ADS_SECURE_AUTHENTICATION, &IID_IADs, (void **)&ads2);
770
if (hr == S_OK)
771
{
772
get_attribute(ads2, (BSTR)L"objectClass", VT_ARRAY | VT_VARIANT);
773
774
hr = IADs_get_Schema(ads2, &bstr);
775
ok(hr == S_OK, "got %#lx\n", hr);
776
trace("Schema of %s: %s\n", wine_dbgstr_w(path), wine_dbgstr_w(bstr));
777
778
IADs_Release(ads2);
779
780
hr = ADsOpenObject(bstr, NULL, NULL, ADS_SECURE_AUTHENTICATION, &IID_IADs, (void **)&ads2);
781
ok(hr == S_OK, "got %#lx\n", hr);
782
IADs_Release(ads2);
783
784
SysFreeString(bstr);
785
}
786
else
787
trace("ADsOpenObject(%s) error %#lx\n", wine_dbgstr_w(path), hr);
788
789
VariantClear(&bin);
790
IADsDNWithBinary_Release(dn);
791
VariantClear(&item);
792
}
793
VariantClear(&var);
794
ok(found == 2, "Users or Computers entry not found\n");
795
796
IADs_Release(ads);
797
}
798
799
START_TEST(ldap)
800
{
801
HRESULT hr;
802
803
hr = CoInitialize(NULL);
804
ok(hr == S_OK, "got %#lx\n", hr);
805
806
test_IADs_GetEx();
807
test_LDAP();
808
test_ParseDisplayName();
809
test_DirectorySearch();
810
test_DirectoryObject();
811
812
CoUninitialize();
813
}
814
815