libwine: Remove __wine_main_arg* from the public header.
[wine/zf.git] / dlls / adsldp / adsldp.c
blobc52f71b751aaa1762ca8ea5cc5da2f8664965188
1 /*
2 * Active Directory services LDAP Provider
4 * Copyright 2018 Dmitry Timoshkov
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.
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.
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
21 #include <stdarg.h>
23 #define COBJMACROS
24 #define NONAMELESSUNION
26 #include "windef.h"
27 #include "winbase.h"
28 #include "initguid.h"
29 #include "objbase.h"
30 #include "rpcproxy.h"
31 #include "rpc.h"
32 #include "iads.h"
33 #include "adshlp.h"
34 #include "adserr.h"
35 #define SECURITY_WIN32
36 #include "security.h"
37 #include "dsgetdc.h"
38 #include "lmcons.h"
39 #include "lmapibuf.h"
40 #include "winldap.h"
42 #include "adsldp_private.h"
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(adsldp);
48 #ifndef LDAP_OPT_SERVER_CONTROLS
49 #define LDAP_OPT_SERVER_CONTROLS 0x0012
50 #endif
52 DEFINE_GUID(CLSID_LDAP,0x228d9a81,0xc302,0x11cf,0x9a,0xa4,0x00,0xaa,0x00,0x4a,0x56,0x91);
53 DEFINE_GUID(CLSID_LDAPNamespace,0x228d9a82,0xc302,0x11cf,0x9a,0xa4,0x00,0xaa,0x00,0x4a,0x56,0x91);
55 static HMODULE adsldp_hinst;
57 static HRESULT LDAPNamespace_create(REFIID riid, void **obj);
59 typedef struct
61 IParseDisplayName IParseDisplayName_iface;
62 LONG ref;
63 } LDAP_PARSE;
65 static inline LDAP_PARSE *impl_from_IParseDisplayName(IParseDisplayName *iface)
67 return CONTAINING_RECORD(iface, LDAP_PARSE, IParseDisplayName_iface);
70 static HRESULT WINAPI ldap_QueryInterface(IParseDisplayName *iface, REFIID riid, void **obj)
72 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), obj);
74 if (!riid || !obj) return E_INVALIDARG;
76 if (IsEqualGUID(riid, &IID_IUnknown) ||
77 IsEqualGUID(riid, &IID_IParseDisplayName))
79 IParseDisplayName_AddRef(iface);
80 *obj = iface;
81 return S_OK;
84 *obj = NULL;
85 FIXME("interface %s is not implemented\n", debugstr_guid(riid));
86 return E_NOINTERFACE;
89 static ULONG WINAPI ldap_AddRef(IParseDisplayName *iface)
91 LDAP_PARSE *ldap = impl_from_IParseDisplayName(iface);
92 return InterlockedIncrement(&ldap->ref);
95 static ULONG WINAPI ldap_Release(IParseDisplayName *iface)
97 LDAP_PARSE *ldap = impl_from_IParseDisplayName(iface);
98 LONG ref = InterlockedDecrement(&ldap->ref);
100 if (!ref)
102 TRACE("destroying %p\n", iface);
103 heap_free(ldap);
106 return ref;
109 static HRESULT WINAPI ldap_ParseDisplayName(IParseDisplayName *iface, IBindCtx *bc,
110 LPOLESTR name, ULONG *eaten, IMoniker **mk)
112 HRESULT hr;
113 IADsOpenDSObject *ads_open;
114 IDispatch *disp;
116 TRACE("%p,%p,%s,%p,%p\n", iface, bc, debugstr_w(name), eaten, mk);
118 hr = LDAPNamespace_create(&IID_IADsOpenDSObject, (void **)&ads_open);
119 if (hr != S_OK) return hr;
121 hr = IADsOpenDSObject_OpenDSObject(ads_open, name, NULL, NULL, ADS_SECURE_AUTHENTICATION, &disp);
122 if (hr != S_OK)
123 hr = IADsOpenDSObject_OpenDSObject(ads_open, name, NULL, NULL, 0, &disp);
124 if (hr == S_OK)
126 hr = CreatePointerMoniker((IUnknown *)disp, mk);
127 if (hr == S_OK)
128 *eaten = wcslen(name);
130 IDispatch_Release(disp);
133 IADsOpenDSObject_Release(ads_open);
135 return hr;
138 static const IParseDisplayNameVtbl LDAP_PARSE_vtbl =
140 ldap_QueryInterface,
141 ldap_AddRef,
142 ldap_Release,
143 ldap_ParseDisplayName
146 static HRESULT LDAP_create(REFIID riid, void **obj)
148 LDAP_PARSE *ldap;
149 HRESULT hr;
151 ldap = heap_alloc(sizeof(*ldap));
152 if (!ldap) return E_OUTOFMEMORY;
154 ldap->IParseDisplayName_iface.lpVtbl = &LDAP_PARSE_vtbl;
155 ldap->ref = 1;
157 hr = IParseDisplayName_QueryInterface(&ldap->IParseDisplayName_iface, riid, obj);
158 IParseDisplayName_Release(&ldap->IParseDisplayName_iface);
160 return hr;
163 typedef struct
165 IADsADSystemInfo IADsADSystemInfo_iface;
166 LONG ref;
167 } AD_sysinfo;
169 static inline AD_sysinfo *impl_from_IADsADSystemInfo(IADsADSystemInfo *iface)
171 return CONTAINING_RECORD(iface, AD_sysinfo, IADsADSystemInfo_iface);
174 static HRESULT WINAPI sysinfo_QueryInterface(IADsADSystemInfo *iface, REFIID riid, void **obj)
176 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), obj);
178 if (!riid || !obj) return E_INVALIDARG;
180 if (IsEqualGUID(riid, &IID_IADsADSystemInfo) ||
181 IsEqualGUID(riid, &IID_IDispatch) ||
182 IsEqualGUID(riid, &IID_IUnknown))
184 IADsADSystemInfo_AddRef(iface);
185 *obj = iface;
186 return S_OK;
189 *obj = NULL;
190 FIXME("interface %s is not implemented\n", debugstr_guid(riid));
191 return E_NOINTERFACE;
194 static ULONG WINAPI sysinfo_AddRef(IADsADSystemInfo *iface)
196 AD_sysinfo *sysinfo = impl_from_IADsADSystemInfo(iface);
197 return InterlockedIncrement(&sysinfo->ref);
200 static ULONG WINAPI sysinfo_Release(IADsADSystemInfo *iface)
202 AD_sysinfo *sysinfo = impl_from_IADsADSystemInfo(iface);
203 LONG ref = InterlockedDecrement(&sysinfo->ref);
205 if (!ref)
207 TRACE("destroying %p\n", iface);
208 heap_free(sysinfo);
211 return ref;
214 static HRESULT WINAPI sysinfo_GetTypeInfoCount(IADsADSystemInfo *iface, UINT *count)
216 FIXME("%p,%p: stub\n", iface, count);
217 return E_NOTIMPL;
220 static HRESULT WINAPI sysinfo_GetTypeInfo(IADsADSystemInfo *iface, UINT index, LCID lcid, ITypeInfo **info)
222 FIXME("%p,%u,%#x,%p: stub\n", iface, index, lcid, info);
223 return E_NOTIMPL;
226 static HRESULT WINAPI sysinfo_GetIDsOfNames(IADsADSystemInfo *iface, REFIID riid, LPOLESTR *names,
227 UINT count, LCID lcid, DISPID *dispid)
229 FIXME("%p,%s,%p,%u,%u,%p: stub\n", iface, debugstr_guid(riid), names, count, lcid, dispid);
230 return E_NOTIMPL;
233 static HRESULT WINAPI sysinfo_Invoke(IADsADSystemInfo *iface, DISPID dispid, REFIID riid, LCID lcid, WORD flags,
234 DISPPARAMS *params, VARIANT *result, EXCEPINFO *excepinfo, UINT *argerr)
236 FIXME("%p,%d,%s,%04x,%04x,%p,%p,%p,%p: stub\n", iface, dispid, debugstr_guid(riid), lcid, flags,
237 params, result, excepinfo, argerr);
238 return E_NOTIMPL;
241 static HRESULT WINAPI sysinfo_get_UserName(IADsADSystemInfo *iface, BSTR *retval)
243 FIXME("%p,%p: stub\n", iface, retval);
244 return E_NOTIMPL;
247 static HRESULT WINAPI sysinfo_get_ComputerName(IADsADSystemInfo *iface, BSTR *retval)
249 UINT size;
250 WCHAR *name;
252 TRACE("%p,%p\n", iface, retval);
254 size = 0;
255 GetComputerObjectNameW(NameFullyQualifiedDN, NULL, &size);
256 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
257 return HRESULT_FROM_WIN32(GetLastError());
259 name = SysAllocStringLen(NULL, size);
260 if (!name) return E_OUTOFMEMORY;
262 if (!GetComputerObjectNameW(NameFullyQualifiedDN, name, &size))
264 SysFreeString(name);
265 return HRESULT_FROM_WIN32(GetLastError());
268 *retval = name;
269 return S_OK;
272 static HRESULT WINAPI sysinfo_get_SiteName(IADsADSystemInfo *iface, BSTR *retval)
274 FIXME("%p,%p: stub\n", iface, retval);
275 return E_NOTIMPL;
278 static HRESULT WINAPI sysinfo_get_DomainShortName(IADsADSystemInfo *iface, BSTR *retval)
280 FIXME("%p,%p: stub\n", iface, retval);
281 return E_NOTIMPL;
284 static HRESULT WINAPI sysinfo_get_DomainDNSName(IADsADSystemInfo *iface, BSTR *retval)
286 FIXME("%p,%p: stub\n", iface, retval);
287 return E_NOTIMPL;
290 static HRESULT WINAPI sysinfo_get_ForestDNSName(IADsADSystemInfo *iface, BSTR *retval)
292 FIXME("%p,%p: stub\n", iface, retval);
293 return E_NOTIMPL;
296 static HRESULT WINAPI sysinfo_get_PDCRoleOwner(IADsADSystemInfo *iface, BSTR *retval)
298 FIXME("%p,%p: stub\n", iface, retval);
299 return E_NOTIMPL;
302 static HRESULT WINAPI sysinfo_get_SchemaRoleOwner(IADsADSystemInfo *iface, BSTR *retval)
304 FIXME("%p,%p: stub\n", iface, retval);
305 return E_NOTIMPL;
308 static HRESULT WINAPI sysinfo_get_IsNativeMode(IADsADSystemInfo *iface, VARIANT_BOOL *retval)
310 FIXME("%p,%p: stub\n", iface, retval);
311 return E_NOTIMPL;
314 static HRESULT WINAPI sysinfo_GetAnyDCName(IADsADSystemInfo *iface, BSTR *retval)
316 FIXME("%p,%p: stub\n", iface, retval);
317 return E_NOTIMPL;
320 static HRESULT WINAPI sysinfo_GetDCSiteName(IADsADSystemInfo *iface, BSTR server, BSTR *retval)
322 FIXME("%p,%s,%p: stub\n", iface, debugstr_w(server), retval);
323 return E_NOTIMPL;
326 static HRESULT WINAPI sysinfo_RefreshSchemaCache(IADsADSystemInfo *iface)
328 FIXME("%p: stub\n", iface);
329 return E_NOTIMPL;
332 static HRESULT WINAPI sysinfo_GetTrees(IADsADSystemInfo *iface, VARIANT *retval)
334 FIXME("%p,%p: stub\n", iface, retval);
335 return E_NOTIMPL;
338 static const IADsADSystemInfoVtbl IADsADSystemInfo_vtbl =
340 sysinfo_QueryInterface,
341 sysinfo_AddRef,
342 sysinfo_Release,
343 sysinfo_GetTypeInfoCount,
344 sysinfo_GetTypeInfo,
345 sysinfo_GetIDsOfNames,
346 sysinfo_Invoke,
347 sysinfo_get_UserName,
348 sysinfo_get_ComputerName,
349 sysinfo_get_SiteName,
350 sysinfo_get_DomainShortName,
351 sysinfo_get_DomainDNSName,
352 sysinfo_get_ForestDNSName,
353 sysinfo_get_PDCRoleOwner,
354 sysinfo_get_SchemaRoleOwner,
355 sysinfo_get_IsNativeMode,
356 sysinfo_GetAnyDCName,
357 sysinfo_GetDCSiteName,
358 sysinfo_RefreshSchemaCache,
359 sysinfo_GetTrees
362 static HRESULT ADSystemInfo_create(REFIID riid, void **obj)
364 AD_sysinfo *sysinfo;
365 HRESULT hr;
367 sysinfo = heap_alloc(sizeof(*sysinfo));
368 if (!sysinfo) return E_OUTOFMEMORY;
370 sysinfo->IADsADSystemInfo_iface.lpVtbl = &IADsADSystemInfo_vtbl;
371 sysinfo->ref = 1;
373 hr = IADsADSystemInfo_QueryInterface(&sysinfo->IADsADSystemInfo_iface, riid, obj);
374 IADsADSystemInfo_Release(&sysinfo->IADsADSystemInfo_iface);
376 return hr;
379 struct ldap_attribute
381 WCHAR *name;
382 WCHAR **values;
385 typedef struct
387 IADs IADs_iface;
388 IADsOpenDSObject IADsOpenDSObject_iface;
389 IDirectorySearch IDirectorySearch_iface;
390 IDirectoryObject IDirectoryObject_iface;
391 LONG ref;
392 LDAP *ld;
393 BSTR host;
394 BSTR object;
395 ULONG port;
396 ULONG attrs_count, attrs_count_allocated;
397 struct ldap_attribute *attrs;
398 struct attribute_type *at;
399 ULONG at_single_count, at_multiple_count;
400 struct
402 ADS_SCOPEENUM scope;
403 int pagesize;
404 BOOL cache_results;
405 BOOL attribtypes_only;
406 BOOL tombstone;
407 } search;
408 } LDAP_namespace;
410 struct ldap_search_context
412 LDAPMessage *res, *entry;
413 BerElement *ber;
414 ULONG count, pos;
415 BOOL add_ADsPath;
418 static inline LDAP_namespace *impl_from_IADs(IADs *iface)
420 return CONTAINING_RECORD(iface, LDAP_namespace, IADs_iface);
423 static HRESULT WINAPI ldapns_QueryInterface(IADs *iface, REFIID riid, void **obj)
425 LDAP_namespace *ldap = impl_from_IADs(iface);
427 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), obj);
429 if (!riid || !obj) return E_INVALIDARG;
431 if (IsEqualGUID(riid, &IID_IUnknown) ||
432 IsEqualGUID(riid, &IID_IDispatch) ||
433 IsEqualGUID(riid, &IID_IADs))
435 IADs_AddRef(iface);
436 *obj = iface;
437 return S_OK;
440 if (IsEqualGUID(riid, &IID_IADsOpenDSObject))
442 IADs_AddRef(iface);
443 *obj = &ldap->IADsOpenDSObject_iface;
444 return S_OK;
447 if (IsEqualGUID(riid, &IID_IDirectorySearch))
449 if (!ldap->ld || (ldap->object && !wcsicmp(ldap->object, L"rootDSE")))
450 return E_NOINTERFACE;
452 IADs_AddRef(iface);
453 *obj = &ldap->IDirectorySearch_iface;
454 return S_OK;
457 if (IsEqualGUID(riid, &IID_IDirectoryObject))
459 IADs_AddRef(iface);
460 *obj = &ldap->IDirectoryObject_iface;
461 return S_OK;
464 FIXME("interface %s is not implemented\n", debugstr_guid(riid));
465 return E_NOINTERFACE;
468 static ULONG WINAPI ldapns_AddRef(IADs *iface)
470 LDAP_namespace *ldap = impl_from_IADs(iface);
471 return InterlockedIncrement(&ldap->ref);
474 static void free_attributes(LDAP_namespace *ldap)
476 ULONG i;
478 if (!ldap->attrs) return;
480 for (i = 0; i < ldap->attrs_count; i++)
482 ldap_memfreeW(ldap->attrs[i].name);
483 ldap_value_freeW(ldap->attrs[i].values);
486 heap_free(ldap->attrs);
487 ldap->attrs = NULL;
488 ldap->attrs_count = 0;
491 static ULONG WINAPI ldapns_Release(IADs *iface)
493 LDAP_namespace *ldap = impl_from_IADs(iface);
494 LONG ref = InterlockedDecrement(&ldap->ref);
496 if (!ref)
498 TRACE("destroying %p\n", iface);
499 if (ldap->ld) ldap_unbind(ldap->ld);
500 SysFreeString(ldap->host);
501 SysFreeString(ldap->object);
502 free_attributes(ldap);
503 free_attribute_types(ldap->at, ldap->at_single_count + ldap->at_multiple_count);
504 heap_free(ldap);
507 return ref;
510 static HRESULT WINAPI ldapns_GetTypeInfoCount(IADs *iface, UINT *count)
512 FIXME("%p,%p: stub\n", iface, count);
513 return E_NOTIMPL;
516 static HRESULT WINAPI ldapns_GetTypeInfo(IADs *iface, UINT index, LCID lcid, ITypeInfo **info)
518 FIXME("%p,%u,%#x,%p: stub\n", iface, index, lcid, info);
519 return E_NOTIMPL;
522 static HRESULT WINAPI ldapns_GetIDsOfNames(IADs *iface, REFIID riid, LPOLESTR *names,
523 UINT count, LCID lcid, DISPID *dispid)
525 FIXME("%p,%s,%p,%u,%u,%p: stub\n", iface, debugstr_guid(riid), names, count, lcid, dispid);
526 return E_NOTIMPL;
529 static HRESULT WINAPI ldapns_Invoke(IADs *iface, DISPID dispid, REFIID riid, LCID lcid, WORD flags,
530 DISPPARAMS *params, VARIANT *result, EXCEPINFO *excepinfo, UINT *argerr)
532 FIXME("%p,%d,%s,%04x,%04x,%p,%p,%p,%p: stub\n", iface, dispid, debugstr_guid(riid), lcid, flags,
533 params, result, excepinfo, argerr);
534 return E_NOTIMPL;
537 static HRESULT WINAPI ldapns_get_Name(IADs *iface, BSTR *retval)
539 FIXME("%p,%p: stub\n", iface, retval);
540 return E_NOTIMPL;
543 static HRESULT WINAPI ldapns_get_Class(IADs *iface, BSTR *retval)
545 FIXME("%p,%p: stub\n", iface, retval);
546 return E_NOTIMPL;
549 static HRESULT WINAPI ldapns_get_GUID(IADs *iface, BSTR *retval)
551 FIXME("%p,%p: stub\n", iface, retval);
552 return E_NOTIMPL;
555 static HRESULT WINAPI ldapns_get_ADsPath(IADs *iface, BSTR *retval)
557 FIXME("%p,%p: stub\n", iface, retval);
558 return E_NOTIMPL;
561 static HRESULT WINAPI ldapns_get_Parent(IADs *iface, BSTR *retval)
563 FIXME("%p,%p: stub\n", iface, retval);
564 return E_NOTIMPL;
567 static HRESULT WINAPI ldapns_get_Schema(IADs *iface, BSTR *retval)
569 FIXME("%p,%p: stub\n", iface, retval);
570 return E_NOTIMPL;
573 static HRESULT WINAPI ldapns_GetInfo(IADs *iface)
575 HRESULT hr;
576 VARIANT var;
578 TRACE("%p\n", iface);
580 hr = ADsBuildVarArrayStr(NULL, 0, &var);
581 if (hr == S_OK)
583 hr = IADs_GetInfoEx(iface, var, 0);
584 VariantClear(&var);
586 return hr;
589 static HRESULT WINAPI ldapns_SetInfo(IADs *iface)
591 FIXME("%p: stub\n", iface);
592 return E_NOTIMPL;
595 static HRESULT WINAPI ldapns_Get(IADs *iface, BSTR name, VARIANT *prop)
597 LDAP_namespace *ldap = impl_from_IADs(iface);
598 HRESULT hr;
599 ULONG i;
601 TRACE("%p,%s,%p\n", iface, debugstr_w(name), prop);
603 if (!name || !prop) return E_ADS_BAD_PARAMETER;
605 if (!ldap->attrs_count)
607 hr = IADs_GetInfo(iface);
608 if (hr != S_OK) return hr;
611 for (i = 0; i < ldap->attrs_count; i++)
613 if (!wcsicmp(name, ldap->attrs[i].name))
615 LONG count = ldap_count_valuesW(ldap->attrs[i].values);
616 if (!count)
618 V_BSTR(prop) = NULL;
619 V_VT(prop) = VT_BSTR;
620 return S_OK;
623 if (count > 1)
625 SAFEARRAY *sa;
626 VARIANT item;
627 LONG idx;
629 TRACE("attr %s has %u values\n", debugstr_w(ldap->attrs[i].name), count);
631 sa = SafeArrayCreateVector(VT_VARIANT, 0, count);
632 if (!sa) return E_OUTOFMEMORY;
634 for (idx = 0; idx < count; idx++)
636 TRACE("=> %s\n", debugstr_w(ldap->attrs[i].values[idx]));
637 V_VT(&item) = VT_BSTR;
638 V_BSTR(&item) = SysAllocString(ldap->attrs[i].values[idx]);
639 if (!V_BSTR(&item))
641 hr = E_OUTOFMEMORY;
642 goto fail;
645 hr = SafeArrayPutElement(sa, &idx, &item);
646 SysFreeString(V_BSTR(&item));
647 if (hr != S_OK) goto fail;
650 V_VT(prop) = VT_ARRAY | VT_VARIANT;
651 V_ARRAY(prop) = sa;
652 return S_OK;
653 fail:
654 SafeArrayDestroy(sa);
655 return hr;
657 else
659 TRACE("=> %s\n", debugstr_w(ldap->attrs[i].values[0]));
660 V_BSTR(prop) = SysAllocString(ldap->attrs[i].values[0]);
661 if (!V_BSTR(prop)) return E_OUTOFMEMORY;
662 V_VT(prop) = VT_BSTR;
663 return S_OK;
668 return E_ADS_PROPERTY_NOT_FOUND;
671 static HRESULT WINAPI ldapns_Put(IADs *iface, BSTR name, VARIANT prop)
673 FIXME("%p,%s,%s: stub\n", iface, debugstr_w(name), wine_dbgstr_variant(&prop));
674 return E_NOTIMPL;
677 static HRESULT WINAPI ldapns_GetEx(IADs *iface, BSTR name, VARIANT *prop)
679 FIXME("%p,%s,%p: stub\n", iface, debugstr_w(name), prop);
680 return E_NOTIMPL;
683 static HRESULT WINAPI ldapns_PutEx(IADs *iface, LONG code, BSTR name, VARIANT prop)
685 FIXME("%p,%d,%s,%s: stub\n", iface, code, debugstr_w(name), wine_dbgstr_variant(&prop));
686 return E_NOTIMPL;
689 static HRESULT add_attribute(LDAP_namespace *ldap, WCHAR *name, WCHAR **values)
691 struct ldap_attribute *new_attrs;
693 if (!ldap->attrs)
695 ldap->attrs = heap_alloc(256 * sizeof(ldap->attrs[0]));
696 if (!ldap->attrs) return E_OUTOFMEMORY;
697 ldap->attrs_count_allocated = 256;
699 else if (ldap->attrs_count_allocated < ldap->attrs_count + 1)
701 new_attrs = heap_realloc(ldap->attrs, (ldap->attrs_count_allocated * 2) * sizeof(*new_attrs));
702 if (!new_attrs) return E_OUTOFMEMORY;
704 ldap->attrs_count_allocated *= 2;
705 ldap->attrs = new_attrs;
708 ldap->attrs[ldap->attrs_count].name = name;
709 ldap->attrs[ldap->attrs_count].values = values;
710 ldap->attrs_count++;
712 return S_OK;
715 static HRESULT WINAPI ldapns_GetInfoEx(IADs *iface, VARIANT prop, LONG reserved)
717 LDAP_namespace *ldap = impl_from_IADs(iface);
718 HRESULT hr;
719 SAFEARRAY *sa;
720 VARIANT *item;
721 WCHAR **props = NULL, *attr, **values;
722 DWORD i, count, err;
723 LDAPMessage *res = NULL, *entry;
724 BerElement *ber;
726 TRACE("%p,%s,%d\n", iface, wine_dbgstr_variant(&prop), reserved);
728 free_attributes(ldap);
730 if (!ldap->ld) return E_NOTIMPL;
732 if (V_VT(&prop) != (VT_ARRAY | VT_VARIANT))
733 return E_ADS_BAD_PARAMETER;
735 sa = V_ARRAY(&prop);
736 if (sa->cDims != 1)
737 return E_ADS_BAD_PARAMETER;
739 hr = SafeArrayAccessData(sa, (void *)&item);
740 if (hr != S_OK) return hr;
742 count = sa->rgsabound[0].cElements;
743 if (count)
745 props = heap_alloc((count + 1) * sizeof(props[0]));
746 if (!props)
748 hr = E_OUTOFMEMORY;
749 goto exit;
752 for (i = 0; i < count; i++)
754 if (V_VT(&item[i]) != VT_BSTR)
756 hr = E_ADS_BAD_PARAMETER;
757 goto exit;
759 props[i] = V_BSTR(&item[i]);
761 props[sa->rgsabound[0].cElements] = NULL;
764 err = ldap_search_sW(ldap->ld, NULL, LDAP_SCOPE_BASE, (WCHAR *)L"(objectClass=*)", props, FALSE, &res);
765 if (err != LDAP_SUCCESS)
767 TRACE("ldap_search_sW error %#x\n", err);
768 hr = HRESULT_FROM_WIN32(map_ldap_error(err));
769 goto exit;
772 entry = ldap_first_entry(ldap->ld, res);
773 while (entry)
775 attr = ldap_first_attributeW(ldap->ld, entry, &ber);
776 while (attr)
778 TRACE("attr: %s\n", debugstr_w(attr));
780 values = ldap_get_valuesW(ldap->ld, entry, attr);
782 hr = add_attribute(ldap, attr, values);
783 if (hr != S_OK)
785 ldap_value_freeW(values);
786 ldap_memfreeW(attr);
787 goto exit;
790 attr = ldap_next_attributeW(ldap->ld, entry, ber);
793 entry = ldap_next_entry(ldap->ld, res);
796 exit:
797 if (res) ldap_msgfree(res);
798 heap_free(props);
799 SafeArrayUnaccessData(sa);
800 return hr;
803 static const IADsVtbl IADs_vtbl =
805 ldapns_QueryInterface,
806 ldapns_AddRef,
807 ldapns_Release,
808 ldapns_GetTypeInfoCount,
809 ldapns_GetTypeInfo,
810 ldapns_GetIDsOfNames,
811 ldapns_Invoke,
812 ldapns_get_Name,
813 ldapns_get_Class,
814 ldapns_get_GUID,
815 ldapns_get_ADsPath,
816 ldapns_get_Parent,
817 ldapns_get_Schema,
818 ldapns_GetInfo,
819 ldapns_SetInfo,
820 ldapns_Get,
821 ldapns_Put,
822 ldapns_GetEx,
823 ldapns_PutEx,
824 ldapns_GetInfoEx
827 static inline LDAP_namespace *impl_from_IADsOpenDSObject(IADsOpenDSObject *iface)
829 return CONTAINING_RECORD(iface, LDAP_namespace, IADsOpenDSObject_iface);
832 static HRESULT WINAPI openobj_QueryInterface(IADsOpenDSObject *iface, REFIID riid, void **obj)
834 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), obj);
836 if (!riid || !obj) return E_INVALIDARG;
838 if (IsEqualGUID(riid, &IID_IADsOpenDSObject) ||
839 IsEqualGUID(riid, &IID_IDispatch) ||
840 IsEqualGUID(riid, &IID_IUnknown))
842 IADsOpenDSObject_AddRef(iface);
843 *obj = iface;
844 return S_OK;
847 FIXME("interface %s is not implemented\n", debugstr_guid(riid));
848 return E_NOINTERFACE;
851 static ULONG WINAPI openobj_AddRef(IADsOpenDSObject *iface)
853 LDAP_namespace *ldap = impl_from_IADsOpenDSObject(iface);
854 return IADs_AddRef(&ldap->IADs_iface);
857 static ULONG WINAPI openobj_Release(IADsOpenDSObject *iface)
859 LDAP_namespace *ldap = impl_from_IADsOpenDSObject(iface);
860 return IADs_Release(&ldap->IADs_iface);
863 static HRESULT WINAPI openobj_GetTypeInfoCount(IADsOpenDSObject *iface, UINT *count)
865 FIXME("%p,%p: stub\n", iface, count);
866 return E_NOTIMPL;
869 static HRESULT WINAPI openobj_GetTypeInfo(IADsOpenDSObject *iface, UINT index, LCID lcid, ITypeInfo **info)
871 FIXME("%p,%u,%#x,%p: stub\n", iface, index, lcid, info);
872 return E_NOTIMPL;
875 static HRESULT WINAPI openobj_GetIDsOfNames(IADsOpenDSObject *iface, REFIID riid, LPOLESTR *names,
876 UINT count, LCID lcid, DISPID *dispid)
878 FIXME("%p,%s,%p,%u,%u,%p: stub\n", iface, debugstr_guid(riid), names, count, lcid, dispid);
879 return E_NOTIMPL;
882 static HRESULT WINAPI openobj_Invoke(IADsOpenDSObject *iface, DISPID dispid, REFIID riid, LCID lcid, WORD flags,
883 DISPPARAMS *params, VARIANT *result, EXCEPINFO *excepinfo, UINT *argerr)
885 FIXME("%p,%d,%s,%04x,%04x,%p,%p,%p,%p: stub\n", iface, dispid, debugstr_guid(riid), lcid, flags,
886 params, result, excepinfo, argerr);
887 return E_NOTIMPL;
890 static HRESULT parse_path(WCHAR *path, BSTR *host, ULONG *port, BSTR *object)
892 WCHAR *p, *p_host;
893 int host_len;
895 if (host) *host = NULL;
896 if (port) *port = 0;
897 if (object) *object = NULL;
899 if (wcsnicmp(path, L"LDAP:", 5) != 0)
900 return E_ADS_BAD_PATHNAME;
902 p = path + 5;
903 if (!*p) return S_OK;
905 if (*p++ != '/' || *p++ != '/' || !*p)
906 return E_ADS_BAD_PATHNAME;
908 p_host = p;
909 host_len = 0;
910 while (*p && *p != '/')
912 if (*p == ':')
914 ULONG dummy;
915 if (!port) port = &dummy;
916 *port = wcstol(p + 1, &p, 10);
917 if (*p && *p != '/') return E_ADS_BAD_PATHNAME;
919 else
921 p++;
922 host_len++;
925 if (host_len == 0) return E_ADS_BAD_PATHNAME;
927 if (host)
929 *host = SysAllocStringLen(p_host, host_len);
930 if (!*host) return E_OUTOFMEMORY;
933 if (!*p) return S_OK;
935 if (*p++ != '/' || !*p)
937 SysFreeString(*host);
938 return E_ADS_BAD_PATHNAME;
941 if (object)
943 *object = SysAllocString(p);
944 if (!*object)
946 SysFreeString(*host);
947 return E_OUTOFMEMORY;
951 return S_OK;
954 static HRESULT WINAPI openobj_OpenDSObject(IADsOpenDSObject *iface, BSTR path, BSTR user, BSTR password,
955 LONG flags, IDispatch **obj)
957 BSTR host, object;
958 ULONG port;
959 IADs *ads;
960 LDAP *ld = NULL;
961 HRESULT hr;
962 ULONG err, at_single_count = 0, at_multiple_count = 0;
963 struct attribute_type *at = NULL;
965 TRACE("%p,%s,%s,%p,%08x,%p\n", iface, debugstr_w(path), debugstr_w(user), password, flags, obj);
967 hr = parse_path(path, &host, &port, &object);
968 if (hr != S_OK) return hr;
970 TRACE("host %s, port %u, object %s\n", debugstr_w(host), port, debugstr_w(object));
972 if (host)
974 int version;
976 if (!wcsicmp(host, L"rootDSE"))
978 DOMAIN_CONTROLLER_INFOW *dcinfo;
980 if (object)
982 hr = E_ADS_BAD_PATHNAME;
983 goto fail;
986 object = host;
988 err = DsGetDcNameW(NULL, NULL, NULL, NULL, DS_RETURN_DNS_NAME, &dcinfo);
989 if (err != ERROR_SUCCESS)
991 hr = HRESULT_FROM_WIN32(LdapGetLastError());
992 goto fail;
995 host = SysAllocString(dcinfo->DomainName);
996 NetApiBufferFree(dcinfo);
998 if (!host)
1000 hr = E_OUTOFMEMORY;
1001 goto fail;
1005 ld = ldap_initW(host, port);
1006 if (!ld)
1008 hr = HRESULT_FROM_WIN32(LdapGetLastError());
1009 goto fail;
1012 version = LDAP_VERSION3;
1013 err = ldap_set_optionW(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1014 if (err != LDAP_SUCCESS)
1016 hr = HRESULT_FROM_WIN32(map_ldap_error(err));
1017 ldap_unbind(ld);
1018 goto fail;
1021 err = ldap_connect(ld, NULL);
1022 if (err != LDAP_SUCCESS)
1024 hr = HRESULT_FROM_WIN32(map_ldap_error(err));
1025 ldap_unbind(ld);
1026 goto fail;
1029 if (flags & ADS_SECURE_AUTHENTICATION)
1031 SEC_WINNT_AUTH_IDENTITY_W id;
1033 id.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1034 id.Domain = (unsigned short *)host;
1035 id.DomainLength = wcslen(host);
1036 id.User = (unsigned short *)user;
1037 id.UserLength = user ? wcslen(user) : 0;
1038 id.Password = (unsigned short *)password;
1039 id.PasswordLength = password ? wcslen(password) : 0;
1041 err = ldap_bind_sW(ld, NULL, (WCHAR *)&id, LDAP_AUTH_NEGOTIATE);
1042 if (err != LDAP_SUCCESS)
1044 TRACE("ldap_bind_sW error %#x\n", err);
1045 hr = HRESULT_FROM_WIN32(map_ldap_error(err));
1046 ldap_unbind(ld);
1047 goto fail;
1050 else
1052 err = ldap_simple_bind_sW(ld, user, password);
1053 if (err != LDAP_SUCCESS)
1055 TRACE("ldap_simple_bind_sW error %#x\n", err);
1056 hr = HRESULT_FROM_WIN32(map_ldap_error(err));
1057 ldap_unbind(ld);
1058 goto fail;
1062 at = load_schema(ld, &at_single_count, &at_multiple_count);
1065 hr = LDAPNamespace_create(&IID_IADs, (void **)&ads);
1066 if (hr == S_OK)
1068 LDAP_namespace *ldap = impl_from_IADs(ads);
1069 ldap->ld = ld;
1070 ldap->host = host;
1071 ldap->port = port;
1072 ldap->object = object;
1073 ldap->at = at;
1074 ldap->at_single_count = at_single_count;
1075 ldap->at_multiple_count = at_multiple_count;
1076 hr = IADs_QueryInterface(ads, &IID_IDispatch, (void **)obj);
1077 IADs_Release(ads);
1078 return hr;
1081 fail:
1082 SysFreeString(host);
1083 SysFreeString(object);
1085 return hr;
1088 static const IADsOpenDSObjectVtbl IADsOpenDSObject_vtbl =
1090 openobj_QueryInterface,
1091 openobj_AddRef,
1092 openobj_Release,
1093 openobj_GetTypeInfoCount,
1094 openobj_GetTypeInfo,
1095 openobj_GetIDsOfNames,
1096 openobj_Invoke,
1097 openobj_OpenDSObject
1100 static inline LDAP_namespace *impl_from_IDirectorySearch(IDirectorySearch *iface)
1102 return CONTAINING_RECORD(iface, LDAP_namespace, IDirectorySearch_iface);
1105 static HRESULT WINAPI search_QueryInterface(IDirectorySearch *iface, REFIID riid, void **obj)
1107 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), obj);
1109 if (!riid || !obj) return E_INVALIDARG;
1111 if (IsEqualGUID(riid, &IID_IDirectorySearch) ||
1112 IsEqualGUID(riid, &IID_IUnknown))
1114 IDirectorySearch_AddRef(iface);
1115 *obj = iface;
1116 return S_OK;
1119 FIXME("interface %s is not implemented\n", debugstr_guid(riid));
1120 return E_NOINTERFACE;
1123 static ULONG WINAPI search_AddRef(IDirectorySearch *iface)
1125 LDAP_namespace *ldap = impl_from_IDirectorySearch(iface);
1126 return IADs_AddRef(&ldap->IADs_iface);
1129 static ULONG WINAPI search_Release(IDirectorySearch *iface)
1131 LDAP_namespace *ldap = impl_from_IDirectorySearch(iface);
1132 return IADs_Release(&ldap->IADs_iface);
1135 static HRESULT WINAPI search_SetSearchPreference(IDirectorySearch *iface, PADS_SEARCHPREF_INFO prefs, DWORD count)
1137 LDAP_namespace *ldap = impl_from_IDirectorySearch(iface);
1138 HRESULT hr = S_OK;
1139 DWORD i;
1141 TRACE("%p,%p,%u\n", iface, prefs, count);
1143 for (i = 0; i < count; i++)
1145 switch (prefs[i].dwSearchPref)
1147 case ADS_SEARCHPREF_SEARCH_SCOPE:
1148 if (prefs[i].vValue.dwType != ADSTYPE_INTEGER)
1150 FIXME("ADS_SEARCHPREF_SEACH_SCOPE: not supportd dwType %d\n", prefs[i].vValue.dwType);
1151 prefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREFVALUE;
1152 break;
1155 switch (prefs[i].vValue.u.Integer)
1157 case ADS_SCOPE_BASE:
1158 case ADS_SCOPE_ONELEVEL:
1159 case ADS_SCOPE_SUBTREE:
1160 TRACE("SEARCH_SCOPE: %d\n", prefs[i].vValue.u.Integer);
1161 ldap->search.scope = prefs[i].vValue.u.Integer;
1162 prefs[i].dwStatus = ADS_STATUS_S_OK;
1163 break;
1165 default:
1166 prefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREFVALUE;
1167 break;
1169 break;
1171 case ADS_SEARCHPREF_SECURITY_MASK:
1173 int security_mask;
1174 ULONG err;
1175 BerElement *ber;
1176 struct berval *berval;
1177 LDAPControlW *ctrls[2], mask;
1179 if (prefs[i].vValue.dwType != ADSTYPE_INTEGER)
1181 FIXME("ADS_SEARCHPREF_SECURITY_MASK: not supportd dwType %d\n", prefs[i].vValue.dwType);
1182 prefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREFVALUE;
1183 break;
1186 TRACE("SECURITY_MASK: %08x\n", prefs[i].vValue.u.Integer);
1187 security_mask = prefs[i].vValue.u.Integer;
1188 if (!security_mask)
1189 security_mask = ADS_SECURITY_INFO_OWNER;
1191 ber = ber_alloc_t(LBER_USE_DER);
1192 if (!ber || ber_printf(ber, (char *)"{i}", security_mask) == -1 || ber_flatten(ber, &berval) == -1)
1194 ber_free(ber, 1);
1195 prefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREF;
1196 break;
1198 TRACE("ber: %s\n", debugstr_an(berval->bv_val, berval->bv_len));
1200 mask.ldctl_oid = (WCHAR *)L"1.2.840.113556.1.4.801";
1201 mask.ldctl_iscritical = TRUE;
1202 mask.ldctl_value.bv_val = berval->bv_val;
1203 mask.ldctl_value.bv_len = berval->bv_len;
1204 ctrls[0] = &mask;
1205 ctrls[1] = NULL;
1206 err = ldap_set_optionW(ldap->ld, LDAP_OPT_SERVER_CONTROLS, ctrls);
1207 if (err != LDAP_SUCCESS)
1209 TRACE("ldap_set_option error %#x\n", err);
1210 prefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREF;
1211 hr = S_ADS_ERRORSOCCURRED;
1213 else
1214 prefs[i].dwStatus = ADS_STATUS_S_OK;
1216 ber_bvfree(berval);
1217 ber_free(ber, 1);
1218 break;
1221 case ADS_SEARCHPREF_PAGESIZE:
1222 if (prefs[i].vValue.dwType != ADSTYPE_INTEGER)
1224 FIXME("ADS_SEARCHPREF_PAGESIZE: not supportd dwType %d\n", prefs[i].vValue.dwType);
1225 prefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREFVALUE;
1226 break;
1229 TRACE("PAGESIZE: %d\n", prefs[i].vValue.u.Integer);
1230 ldap->search.pagesize = prefs[i].vValue.u.Integer;
1231 prefs[i].dwStatus = ADS_STATUS_S_OK;
1232 break;
1234 case ADS_SEARCHPREF_CACHE_RESULTS:
1235 if (prefs[i].vValue.dwType != ADSTYPE_BOOLEAN)
1237 FIXME("ADS_SEARCHPREF_CACHE_RESULTS: not supportd dwType %d\n", prefs[i].vValue.dwType);
1238 prefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREFVALUE;
1239 break;
1242 TRACE("CACHE_RESULTS: %d\n", prefs[i].vValue.u.Boolean);
1243 ldap->search.cache_results = prefs[i].vValue.u.Boolean;
1244 prefs[i].dwStatus = ADS_STATUS_S_OK;
1245 break;
1247 case ADS_SEARCHPREF_ATTRIBTYPES_ONLY:
1248 if (prefs[i].vValue.dwType != ADSTYPE_BOOLEAN)
1250 FIXME("ADS_SEARCHPREF_ATTRIBTYPES_ONLY: not supportd dwType %d\n", prefs[i].vValue.dwType);
1251 prefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREFVALUE;
1252 break;
1255 TRACE("ATTRIBTYPES_ONLY: %d\n", prefs[i].vValue.u.Boolean);
1256 ldap->search.attribtypes_only = prefs[i].vValue.u.Boolean;
1257 prefs[i].dwStatus = ADS_STATUS_S_OK;
1258 break;
1260 case ADS_SEARCHPREF_TOMBSTONE:
1261 if (prefs[i].vValue.dwType != ADSTYPE_BOOLEAN)
1263 FIXME("ADS_SEARCHPREF_TOMBSTONE: not supportd dwType %d\n", prefs[i].vValue.dwType);
1264 prefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREFVALUE;
1265 break;
1268 TRACE("TOMBSTONE: %d\n", prefs[i].vValue.u.Boolean);
1269 ldap->search.tombstone = prefs[i].vValue.u.Boolean;
1270 prefs[i].dwStatus = ADS_STATUS_S_OK;
1271 break;
1273 default:
1274 FIXME("pref %d, type %u: stub\n", prefs[i].dwSearchPref, prefs[i].vValue.dwType);
1275 prefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREF;
1276 break;
1280 return hr;
1283 static HRESULT WINAPI search_ExecuteSearch(IDirectorySearch *iface, LPWSTR filter, LPWSTR *names,
1284 DWORD count, PADS_SEARCH_HANDLE res)
1286 LDAP_namespace *ldap = impl_from_IDirectorySearch(iface);
1287 ULONG err, i;
1288 WCHAR **props;
1289 struct ldap_search_context *ldap_ctx;
1291 TRACE("%p,%s,%p,%u,%p\n", iface, debugstr_w(filter), names, count, res);
1293 if (!res) return E_ADS_BAD_PARAMETER;
1295 ldap_ctx = heap_alloc_zero(sizeof(*ldap_ctx));
1296 if (!ldap_ctx) return E_OUTOFMEMORY;
1298 if (count == 0xffffffff)
1299 props = NULL;
1300 else
1302 if (count && !names) return E_ADS_BAD_PARAMETER;
1304 props = heap_alloc((count + 1) * sizeof(props[0]));
1305 if (!props)
1307 heap_free(ldap_ctx);
1308 return E_OUTOFMEMORY;
1311 for (i = 0; i < count; i++)
1313 TRACE("=> %s\n", debugstr_w(names[i]));
1314 props[i] = names[i];
1317 props[count] = NULL;
1320 err = ldap_search_sW(ldap->ld, ldap->object, ldap->search.scope, filter, props, ldap->search.attribtypes_only, &ldap_ctx->res);
1321 heap_free(props);
1322 if (err != LDAP_SUCCESS)
1324 TRACE("ldap_search_sW error %#x\n", err);
1325 heap_free(ldap_ctx);
1326 return HRESULT_FROM_WIN32(map_ldap_error(err));
1329 *res = ldap_ctx;
1330 return S_OK;
1333 static HRESULT WINAPI search_AbandonSearch(IDirectorySearch *iface, ADS_SEARCH_HANDLE res)
1335 FIXME("%p,%p: stub\n", iface, res);
1336 return E_NOTIMPL;
1339 static HRESULT WINAPI search_GetFirstRow(IDirectorySearch *iface, ADS_SEARCH_HANDLE res)
1341 struct ldap_search_context *ldap_ctx = res;
1343 TRACE("%p,%p\n", iface, res);
1345 if (!res) return E_ADS_BAD_PARAMETER;
1347 ldap_ctx->entry = NULL;
1349 return IDirectorySearch_GetNextRow(iface, res);
1352 static HRESULT WINAPI search_GetNextRow(IDirectorySearch *iface, ADS_SEARCH_HANDLE res)
1354 LDAP_namespace *ldap = impl_from_IDirectorySearch(iface);
1355 struct ldap_search_context *ldap_ctx = res;
1357 TRACE("%p,%p\n", iface, res);
1359 if (!res) return E_ADS_BAD_PARAMETER;
1361 if (!ldap_ctx->entry)
1363 ldap_ctx->count = ldap_count_entries(ldap->ld, ldap_ctx->res);
1364 ldap_ctx->pos = 0;
1366 if (ldap_ctx->pos >= ldap_ctx->count)
1367 return S_ADS_NOMORE_ROWS;
1369 ldap_ctx->entry = ldap_first_entry(ldap->ld, ldap_ctx->res);
1371 else
1373 if (ldap_ctx->pos >= ldap_ctx->count)
1374 return S_ADS_NOMORE_ROWS;
1376 ldap_ctx->entry = ldap_next_entry(ldap->ld, ldap_ctx->entry);
1379 if (!ldap_ctx->entry)
1380 return S_ADS_NOMORE_ROWS;
1382 ldap_ctx->pos++;
1383 ldap_ctx->ber = NULL;
1385 return S_OK;
1388 static HRESULT WINAPI search_GetPreviousRow(IDirectorySearch *iface, ADS_SEARCH_HANDLE res)
1390 FIXME("%p,%p: stub\n", iface, res);
1391 return E_NOTIMPL;
1394 static HRESULT WINAPI search_GetNextColumnName(IDirectorySearch *iface, ADS_SEARCH_HANDLE res, LPWSTR *name)
1396 LDAP_namespace *ldap = impl_from_IDirectorySearch(iface);
1397 struct ldap_search_context *ldap_ctx = res;
1398 WCHAR *attr;
1400 TRACE("%p,%p,%p\n", iface, res, name);
1402 if (!name || !ldap_ctx || !ldap_ctx->entry) return E_ADS_BAD_PARAMETER;
1404 if (!ldap_ctx->ber)
1406 attr = ldap_first_attributeW(ldap->ld, ldap_ctx->entry, &ldap_ctx->ber);
1407 ldap_ctx->add_ADsPath = TRUE;
1409 else
1410 attr = ldap_next_attributeW(ldap->ld, ldap_ctx->entry, ldap_ctx->ber);
1412 if (attr)
1414 TRACE("=> %s\n", debugstr_w(attr));
1415 *name = AllocADsStr(attr);
1416 ldap_memfreeW(attr);
1417 return *name ? S_OK : E_OUTOFMEMORY;
1419 else if (ldap_ctx->add_ADsPath)
1421 ldap_ctx->add_ADsPath = FALSE;
1422 *name = AllocADsStr((WCHAR *)L"ADsPath");
1423 TRACE("=> %s\n", debugstr_w(*name));
1424 return *name ? S_OK : E_OUTOFMEMORY;
1427 *name = NULL;
1428 return S_ADS_NOMORE_COLUMNS;
1431 static HRESULT add_column_values(LDAP_namespace *ldap, struct ldap_search_context *ldap_ctx,
1432 LPWSTR name, ADS_SEARCH_COLUMN *col)
1434 ADSTYPEENUM type;
1435 DWORD i, count;
1437 type = get_schema_type(name, ldap->at, ldap->at_single_count, ldap->at_multiple_count);
1438 TRACE("%s => type %d\n", debugstr_w(name), type);
1440 switch (type)
1442 default:
1443 FIXME("no special handling for type %d\n", type);
1444 /* fall through */
1445 case ADSTYPE_DN_STRING:
1446 case ADSTYPE_CASE_EXACT_STRING:
1447 case ADSTYPE_CASE_IGNORE_STRING:
1448 case ADSTYPE_PRINTABLE_STRING:
1450 WCHAR **values = ldap_get_valuesW(ldap->ld, ldap_ctx->entry, name);
1451 if (!values)
1452 return E_ADS_COLUMN_NOT_SET;
1453 count = ldap_count_valuesW(values);
1455 col->pADsValues = heap_alloc_zero(count * sizeof(col->pADsValues[0]));
1456 if (!col->pADsValues)
1458 ldap_value_freeW(values);
1459 return E_OUTOFMEMORY;
1462 for (i = 0; i < count; i++)
1464 TRACE("=> %s\n", debugstr_w(values[i]));
1465 col->pADsValues[i].dwType = type;
1466 col->pADsValues[i].u.CaseIgnoreString = values[i];
1469 col->hReserved = values;
1470 break;
1473 case ADSTYPE_BOOLEAN:
1475 WCHAR **values = ldap_get_valuesW(ldap->ld, ldap_ctx->entry, name);
1476 if (!values)
1477 return E_ADS_COLUMN_NOT_SET;
1478 count = ldap_count_valuesW(values);
1480 col->pADsValues = heap_alloc_zero(count * sizeof(col->pADsValues[0]));
1481 if (!col->pADsValues)
1483 ldap_value_freeW(values);
1484 return E_OUTOFMEMORY;
1487 for (i = 0; i < count; i++)
1489 col->pADsValues[i].dwType = type;
1491 if (wcsicmp(values[i], L"TRUE"))
1492 col->pADsValues[i].u.Boolean = 1;
1493 else if (wcsicmp(values[i], L"FALSE"))
1494 col->pADsValues[i].u.Boolean = 0;
1495 else
1497 FIXME("not recognized boolean value %s\n", debugstr_w(values[i]));
1498 col->pADsValues[i].u.Boolean = 0;
1500 TRACE("=> %d\n", col->pADsValues[i].u.Boolean);
1503 ldap_value_freeW(values);
1504 col->hReserved = NULL;
1505 break;
1508 case ADSTYPE_INTEGER:
1509 case ADSTYPE_LARGE_INTEGER:
1511 struct berval **values = ldap_get_values_lenW(ldap->ld, ldap_ctx->entry, name);
1512 if (!values)
1513 return E_ADS_COLUMN_NOT_SET;
1514 count = ldap_count_values_len(values);
1516 col->pADsValues = heap_alloc_zero(count * sizeof(col->pADsValues[0]));
1517 if (!col->pADsValues)
1519 ldap_value_free_len(values);
1520 return E_OUTOFMEMORY;
1523 for (i = 0; i < count; i++)
1525 col->pADsValues[i].dwType = type;
1527 if (type == ADSTYPE_LARGE_INTEGER)
1529 col->pADsValues[i].u.LargeInteger.QuadPart = _atoi64(values[i]->bv_val);
1530 TRACE("%s => %s\n", debugstr_an(values[i]->bv_val, values[i]->bv_len), wine_dbgstr_longlong(col->pADsValues[i].u.LargeInteger.QuadPart));
1532 else
1534 col->pADsValues[i].u.Integer = atol(values[i]->bv_val);
1535 TRACE("%s => %d\n", debugstr_an(values[i]->bv_val, values[i]->bv_len), col->pADsValues[i].u.Integer);
1539 ldap_value_free_len(values);
1540 col->hReserved = NULL;
1541 break;
1544 case ADSTYPE_OCTET_STRING:
1545 case ADSTYPE_NT_SECURITY_DESCRIPTOR:
1547 struct berval **values = ldap_get_values_lenW(ldap->ld, ldap_ctx->entry, name);
1548 if (!values)
1549 return E_ADS_COLUMN_NOT_SET;
1550 count = ldap_count_values_len(values);
1552 col->pADsValues = heap_alloc_zero(count * sizeof(col->pADsValues[0]));
1553 if (!col->pADsValues)
1555 ldap_value_free_len(values);
1556 return E_OUTOFMEMORY;
1559 for (i = 0; i < count; i++)
1561 TRACE("=> %s\n", debugstr_an(values[i]->bv_val, values[i]->bv_len));
1562 col->pADsValues[i].dwType = type;
1563 col->pADsValues[i].u.OctetString.dwLength = values[i]->bv_len;
1564 col->pADsValues[i].u.OctetString.lpValue = (BYTE *)values[i]->bv_val;
1567 col->hReserved = values;
1568 break;
1571 case ADSTYPE_UTC_TIME:
1573 struct berval **values = ldap_get_values_lenW(ldap->ld, ldap_ctx->entry, name);
1574 if (!values)
1575 return E_ADS_COLUMN_NOT_SET;
1576 count = ldap_count_values_len(values);
1578 col->pADsValues = heap_alloc_zero(count * sizeof(col->pADsValues[0]));
1579 if (!col->pADsValues)
1581 ldap_value_free_len(values);
1582 return E_OUTOFMEMORY;
1585 for (i = 0; i < count; i++)
1587 col->pADsValues[i].dwType = type;
1588 if (values[i]->bv_len < 14 ||
1589 _snscanf_l(values[i]->bv_val, values[i]->bv_len, "%04u%02u%02u%02u%02u%02u", NULL,
1590 &col->pADsValues[i].u.UTCTime.wYear, &col->pADsValues[i].u.UTCTime.wMonth,
1591 &col->pADsValues[i].u.UTCTime.wDay, &col->pADsValues[i].u.UTCTime.wHour,
1592 &col->pADsValues[i].u.UTCTime.wMinute, &col->pADsValues[i].u.UTCTime.wSecond) != 6)
1594 FIXME("not recognized UTCTime: %s\n", debugstr_an(values[i]->bv_val, values[i]->bv_len));
1595 memset(&col->pADsValues[i].u.UTCTime, 0, sizeof(col->pADsValues[i].u.UTCTime));
1596 continue;
1599 if ((values[i]->bv_val[14] != '.' && values[i]->bv_val[14] != ',') ||
1600 values[i]->bv_val[15] != '0' || values[i]->bv_val[16] != 'Z')
1601 FIXME("not handled time zone: %s\n", debugstr_an(values[i]->bv_val + 14, values[i]->bv_len - 14));
1603 TRACE("%s => %02u.%02u.%04u %02u:%02u:%02u\n", debugstr_an(values[i]->bv_val, values[i]->bv_len),
1604 col->pADsValues[i].u.UTCTime.wDay, col->pADsValues[i].u.UTCTime.wMonth,
1605 col->pADsValues[i].u.UTCTime.wYear, col->pADsValues[i].u.UTCTime.wHour,
1606 col->pADsValues[i].u.UTCTime.wMinute, col->pADsValues[i].u.UTCTime.wSecond);
1609 ldap_value_free_len(values);
1610 col->hReserved = NULL;
1611 break;
1614 case ADSTYPE_DN_WITH_BINARY:
1616 static const BYTE hex2bin[] =
1618 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x00 */
1619 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x10 */
1620 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x20 */
1621 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, /* 0x30 */
1622 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0, /* 0x40 */
1623 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x50 */
1624 0,10,11,12,13,14,15 /* 0x60 */
1626 ADS_DN_WITH_BINARY *dnb;
1627 WCHAR **values = ldap_get_valuesW(ldap->ld, ldap_ctx->entry, name);
1628 if (!values)
1629 return E_ADS_COLUMN_NOT_SET;
1630 count = ldap_count_valuesW(values);
1632 col->pADsValues = heap_alloc_zero(count * (sizeof(col->pADsValues[0]) + sizeof(col->pADsValues[0].u.pDNWithBinary[0])));
1633 if (!col->pADsValues)
1635 ldap_value_freeW(values);
1636 return E_OUTOFMEMORY;
1639 dnb = (ADS_DN_WITH_BINARY *)(col->pADsValues + count);
1641 for (i = 0; i < count; i++)
1643 WCHAR *p = values[i];
1644 DWORD n;
1646 col->pADsValues[i].dwType = type;
1647 col->pADsValues[i].u.pDNWithBinary = dnb++;
1649 if ((p[0] != 'b' && p[0] != 'B') || p[1] != ':')
1650 FIXME("wrong DN with binary tag '%c%c'\n", p[0], p[1]);
1651 p += 2;
1653 col->pADsValues[i].u.pDNWithBinary->dwLength = wcstol(p, &p, 10) / 2;
1654 if (*p != ':')
1655 FIXME("wrong DN with binary separator '%c'\n", *p);
1656 p++;
1657 col->pADsValues[i].u.pDNWithBinary->lpBinaryValue = (BYTE *)p;
1658 /* decode values in-place */
1659 for (n = 0; n < col->pADsValues[i].u.pDNWithBinary->dwLength; n++, p += 2)
1661 BYTE b;
1663 if (p[0] > 'f' || (p[0] != '0' && !hex2bin[p[0]]) ||
1664 p[1] > 'f' || (p[1] != '0' && !hex2bin[p[1]]))
1666 FIXME("bad hex encoding at %s\n", debugstr_w(p));
1667 continue;
1670 b = (hex2bin[p[0]] << 4) | hex2bin[p[1]];
1671 col->pADsValues[i].u.pDNWithBinary->lpBinaryValue[n] = b;
1673 if (*p != ':')
1674 FIXME("wrong DN with binary separator '%c'\n", *p);
1675 col->pADsValues[i].u.pDNWithBinary->pszDNString = p + 1;
1677 TRACE("%s => %u,%s,%s\n", debugstr_w(values[i]),
1678 col->pADsValues[i].u.pDNWithBinary->dwLength,
1679 debugstr_an((char *)col->pADsValues[i].u.pDNWithBinary->lpBinaryValue, col->pADsValues[i].u.pDNWithBinary->dwLength),
1680 debugstr_w(col->pADsValues[i].u.pDNWithBinary->pszDNString));
1683 col->hReserved = values;
1684 break;
1688 col->dwADsType = type;
1689 col->dwNumValues = count;
1690 col->pszAttrName = strdupW(name);
1692 return S_OK;
1695 static HRESULT WINAPI search_GetColumn(IDirectorySearch *iface, ADS_SEARCH_HANDLE res,
1696 LPWSTR name, PADS_SEARCH_COLUMN col)
1698 LDAP_namespace *ldap = impl_from_IDirectorySearch(iface);
1699 struct ldap_search_context *ldap_ctx = res;
1700 HRESULT hr;
1701 ULONG count;
1703 TRACE("%p,%p,%s,%p\n", iface, res, debugstr_w(name), col);
1705 if (!res || !name || !ldap_ctx->entry) return E_ADS_BAD_PARAMETER;
1707 memset(col, 0, sizeof(*col));
1709 if (!wcsicmp(name, L"ADsPath"))
1711 WCHAR *dn = ldap_get_dnW(ldap->ld, ldap_ctx->entry);
1713 col->pADsValues = heap_alloc(sizeof(col->pADsValues[0]));
1714 if (!col->pADsValues)
1716 hr = E_OUTOFMEMORY;
1717 goto exit;
1720 count = sizeof(L"LDAP://") + (wcslen(ldap->host) + 1 /* '/' */) * sizeof(WCHAR);
1721 if (dn) count += wcslen(dn) * sizeof(WCHAR);
1723 col->pADsValues[0].u.CaseIgnoreString = heap_alloc(count);
1724 if (!col->pADsValues[0].u.CaseIgnoreString)
1726 hr = E_OUTOFMEMORY;
1727 goto exit;
1730 wcscpy(col->pADsValues[0].u.CaseIgnoreString, L"LDAP://");
1731 wcscat(col->pADsValues[0].u.CaseIgnoreString, ldap->host);
1732 wcscat(col->pADsValues[0].u.CaseIgnoreString, L"/");
1733 if (dn) wcscat(col->pADsValues[0].u.CaseIgnoreString, dn);
1734 col->pADsValues[0].dwType = ADSTYPE_CASE_IGNORE_STRING;
1735 col->dwADsType = ADSTYPE_CASE_IGNORE_STRING;
1736 col->dwNumValues = 1;
1737 col->pszAttrName = strdupW(name);
1738 col->hReserved = NULL;
1740 TRACE("=> %s\n", debugstr_w(col->pADsValues[0].u.CaseIgnoreString));
1741 hr = S_OK;
1742 exit:
1743 ldap_memfreeW(dn);
1744 return hr;
1747 return add_column_values(ldap, ldap_ctx, name, col);
1750 static HRESULT WINAPI search_FreeColumn(IDirectorySearch *iface, PADS_SEARCH_COLUMN col)
1752 TRACE("%p,%p\n", iface, col);
1754 if (!col) return E_ADS_BAD_PARAMETER;
1756 heap_free(col->pADsValues);
1757 heap_free(col->pszAttrName);
1759 if (col->hReserved)
1761 if (col->dwADsType == ADSTYPE_OCTET_STRING)
1762 ldap_value_free_len(col->hReserved);
1763 else
1764 ldap_value_freeW(col->hReserved);
1767 return S_OK;
1770 static HRESULT WINAPI search_CloseSearchHandle(IDirectorySearch *iface, ADS_SEARCH_HANDLE res)
1772 struct ldap_search_context *ldap_ctx = res;
1774 TRACE("%p,%p\n", iface, res);
1776 if (!res) return E_ADS_BAD_PARAMETER;
1778 ldap_msgfree(ldap_ctx->res);
1780 return S_OK;
1783 static const IDirectorySearchVtbl IDirectorySearch_vtbl =
1785 search_QueryInterface,
1786 search_AddRef,
1787 search_Release,
1788 search_SetSearchPreference,
1789 search_ExecuteSearch,
1790 search_AbandonSearch,
1791 search_GetFirstRow,
1792 search_GetNextRow,
1793 search_GetPreviousRow,
1794 search_GetNextColumnName,
1795 search_GetColumn,
1796 search_FreeColumn,
1797 search_CloseSearchHandle
1800 static inline LDAP_namespace *impl_from_IDirectoryObject(IDirectoryObject *iface)
1802 return CONTAINING_RECORD(iface, LDAP_namespace, IDirectoryObject_iface);
1805 static HRESULT WINAPI dirobj_QueryInterface(IDirectoryObject *iface, REFIID riid, void **obj)
1807 LDAP_namespace *ldap = impl_from_IDirectoryObject(iface);
1809 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), obj);
1811 if (!riid || !obj) return E_INVALIDARG;
1813 if (IsEqualGUID(riid, &IID_IDirectoryObject) ||
1814 IsEqualGUID(riid, &IID_IUnknown))
1816 IDirectoryObject_AddRef(iface);
1817 *obj = iface;
1818 return S_OK;
1821 return IADs_QueryInterface(&ldap->IADs_iface, riid, obj);
1824 static ULONG WINAPI dirobj_AddRef(IDirectoryObject *iface)
1826 LDAP_namespace *ldap = impl_from_IDirectoryObject(iface);
1827 return IADs_AddRef(&ldap->IADs_iface);
1830 static ULONG WINAPI dirobj_Release(IDirectoryObject *iface)
1832 LDAP_namespace *ldap = impl_from_IDirectoryObject(iface);
1833 return IADs_Release(&ldap->IADs_iface);
1836 static HRESULT WINAPI dirobj_GetObjectInformation(IDirectoryObject *iface, PADS_OBJECT_INFO *info)
1838 FIXME("%p,%p: stub\n", iface, info);
1839 return E_NOTIMPL;
1842 static HRESULT WINAPI dirobj_GetObjectAttributes(IDirectoryObject *iface, LPWSTR *names,
1843 DWORD count, PADS_ATTR_INFO *attrs, DWORD *count_returned)
1845 FIXME("%p,%p,%u,%p,%p: stub\n", iface, names, count, attrs, count_returned);
1846 return E_NOTIMPL;
1849 static HRESULT WINAPI dirobj_SetObjectAttributes(IDirectoryObject *iface, PADS_ATTR_INFO attrs,
1850 DWORD count, DWORD *count_set)
1852 FIXME("%p,%p,%u,%p: stub\n", iface, attrs, count, count_set);
1853 return E_NOTIMPL;
1856 static HRESULT WINAPI dirobj_CreateDSObject(IDirectoryObject *iface, LPWSTR name,
1857 PADS_ATTR_INFO attrs, DWORD count, IDispatch **obj)
1859 FIXME("%p,%s,%p,%u,%p: stub\n", iface, debugstr_w(name), attrs, count, obj);
1860 return E_NOTIMPL;
1863 static HRESULT WINAPI dirobj_DeleteDSObject(IDirectoryObject *iface, LPWSTR name)
1865 FIXME("%p,%s: stub\n", iface, debugstr_w(name));
1866 return E_NOTIMPL;
1869 static const IDirectoryObjectVtbl IDirectoryObject_vtbl =
1871 dirobj_QueryInterface,
1872 dirobj_AddRef,
1873 dirobj_Release,
1874 dirobj_GetObjectInformation,
1875 dirobj_GetObjectAttributes,
1876 dirobj_SetObjectAttributes,
1877 dirobj_CreateDSObject,
1878 dirobj_DeleteDSObject
1881 static HRESULT LDAPNamespace_create(REFIID riid, void **obj)
1883 LDAP_namespace *ldap;
1884 HRESULT hr;
1886 ldap = heap_alloc(sizeof(*ldap));
1887 if (!ldap) return E_OUTOFMEMORY;
1889 ldap->IADs_iface.lpVtbl = &IADs_vtbl;
1890 ldap->IADsOpenDSObject_iface.lpVtbl = &IADsOpenDSObject_vtbl;
1891 ldap->IDirectorySearch_iface.lpVtbl = &IDirectorySearch_vtbl;
1892 ldap->IDirectoryObject_iface.lpVtbl = &IDirectoryObject_vtbl;
1893 ldap->ref = 1;
1894 ldap->ld = NULL;
1895 ldap->host = NULL;
1896 ldap->object = NULL;
1897 ldap->attrs_count = 0;
1898 ldap->attrs_count_allocated = 0;
1899 ldap->attrs = NULL;
1900 ldap->search.scope = ADS_SCOPE_SUBTREE;
1901 ldap->search.pagesize = 0;
1902 ldap->search.cache_results = TRUE;
1903 ldap->search.attribtypes_only = FALSE;
1904 ldap->search.tombstone = FALSE;
1905 ldap->at = NULL;
1906 ldap->at_single_count = 0;
1907 ldap->at_multiple_count = 0;
1909 hr = IADs_QueryInterface(&ldap->IADs_iface, riid, obj);
1910 IADs_Release(&ldap->IADs_iface);
1912 return hr;
1915 static const struct class_info
1917 const CLSID *clsid;
1918 HRESULT (*constructor)(REFIID, void **);
1919 } class_info[] =
1921 { &CLSID_ADSystemInfo, ADSystemInfo_create },
1922 { &CLSID_LDAP, LDAP_create },
1923 { &CLSID_LDAPNamespace, LDAPNamespace_create },
1926 typedef struct
1928 IClassFactory IClassFactory_iface;
1929 LONG ref;
1930 const struct class_info *info;
1931 } class_factory;
1933 static inline class_factory *impl_from_IClassFactory(IClassFactory *iface)
1935 return CONTAINING_RECORD(iface, class_factory, IClassFactory_iface);
1938 static HRESULT WINAPI factory_QueryInterface(IClassFactory *iface, REFIID riid, LPVOID *obj)
1940 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), obj);
1942 if (!riid || !obj) return E_INVALIDARG;
1944 if (IsEqualIID(riid, &IID_IUnknown) ||
1945 IsEqualIID(riid, &IID_IClassFactory))
1947 IClassFactory_AddRef(iface);
1948 *obj = iface;
1949 return S_OK;
1952 *obj = NULL;
1953 FIXME("interface %s is not implemented\n", debugstr_guid(riid));
1954 return E_NOINTERFACE;
1957 static ULONG WINAPI factory_AddRef(IClassFactory *iface)
1959 class_factory *factory = impl_from_IClassFactory(iface);
1960 ULONG ref = InterlockedIncrement(&factory->ref);
1962 TRACE("(%p) ref %u\n", iface, ref);
1964 return ref;
1967 static ULONG WINAPI factory_Release(IClassFactory *iface)
1969 class_factory *factory = impl_from_IClassFactory(iface);
1970 ULONG ref = InterlockedDecrement(&factory->ref);
1972 TRACE("(%p) ref %u\n", iface, ref);
1974 if (!ref)
1975 heap_free(factory);
1977 return ref;
1980 static HRESULT WINAPI factory_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID riid, void **obj)
1982 class_factory *factory = impl_from_IClassFactory(iface);
1984 TRACE("%p,%s,%p\n", outer, debugstr_guid(riid), obj);
1986 if (!riid || !obj) return E_INVALIDARG;
1988 *obj = NULL;
1989 if (outer) return CLASS_E_NOAGGREGATION;
1991 return factory->info->constructor(riid, obj);
1994 static HRESULT WINAPI factory_LockServer(IClassFactory *iface, BOOL lock)
1996 FIXME("%p,%d: stub\n", iface, lock);
1997 return S_OK;
2000 static const struct IClassFactoryVtbl factory_vtbl =
2002 factory_QueryInterface,
2003 factory_AddRef,
2004 factory_Release,
2005 factory_CreateInstance,
2006 factory_LockServer
2009 static HRESULT factory_constructor(const struct class_info *info, REFIID riid, void **obj)
2011 class_factory *factory;
2012 HRESULT hr;
2014 factory = heap_alloc(sizeof(*factory));
2015 if (!factory) return E_OUTOFMEMORY;
2017 factory->IClassFactory_iface.lpVtbl = &factory_vtbl;
2018 factory->ref = 1;
2019 factory->info = info;
2021 hr = IClassFactory_QueryInterface(&factory->IClassFactory_iface, riid, obj);
2022 IClassFactory_Release(&factory->IClassFactory_iface);
2024 return hr;
2027 HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, LPVOID *obj)
2029 int i;
2031 TRACE("%s,%s,%p\n", debugstr_guid(clsid), debugstr_guid(iid), obj);
2033 if (!clsid || !iid || !obj) return E_INVALIDARG;
2035 *obj = NULL;
2037 for (i = 0; i < ARRAY_SIZE(class_info); i++)
2039 if (IsEqualCLSID(class_info[i].clsid, clsid))
2040 return factory_constructor(&class_info[i], iid, obj);
2043 FIXME("class %s/%s is not implemented\n", debugstr_guid(clsid), debugstr_guid(iid));
2044 return CLASS_E_CLASSNOTAVAILABLE;
2047 HRESULT WINAPI DllCanUnloadNow(void)
2049 return S_FALSE;
2052 HRESULT WINAPI DllRegisterServer(void)
2054 return __wine_register_resources(adsldp_hinst);
2057 HRESULT WINAPI DllUnregisterServer(void)
2059 return __wine_unregister_resources(adsldp_hinst);
2062 BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, void *reserved)
2064 TRACE("%p,%u,%p\n", hinst, reason, reserved);
2066 switch (reason)
2068 case DLL_WINE_PREATTACH:
2069 return FALSE; /* prefer native version */
2071 case DLL_PROCESS_ATTACH:
2072 adsldp_hinst = hinst;
2073 DisableThreadLibraryCalls(hinst);
2074 break;
2077 return TRUE;