Signed-off-by: Dmitry Timoshkov <dmitry(a)baikal.ru>
---
dlls/adsldp/Makefile.in | 3 +-
dlls/adsldp/adsldp.c | 62 +++++++--
dlls/adsldp/adsldp_private.h | 17 ++-
dlls/adsldp/schema.c | 263 +++++++++++++++++++++++++++++++++++
4 files changed, 329 insertions(+), 16 deletions(-)
create mode 100644 dlls/adsldp/schema.c
diff --git a/dlls/adsldp/Makefile.in b/dlls/adsldp/Makefile.in
index 07f79a9993..a99917a16a 100644
--- a/dlls/adsldp/Makefile.in
+++ b/dlls/adsldp/Makefile.in
@@ -6,7 +6,8 @@ EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \
adsldp.c \
- ldap.c
+ ldap.c \
+ schema.c
IDL_SRCS = \
adsldp.idl
diff --git a/dlls/adsldp/adsldp.c b/dlls/adsldp/adsldp.c
index b88867bec1..d3feb89a05 100644
--- a/dlls/adsldp/adsldp.c
+++ b/dlls/adsldp/adsldp.c
@@ -390,6 +390,8 @@ typedef struct
ULONG port;
ULONG attrs_count, attrs_count_allocated;
struct ldap_attribute *attrs;
+ struct attribute_type *at;
+ ULONG at_count;
struct
{
ADS_SCOPEENUM scope;
@@ -479,6 +481,7 @@ static ULONG WINAPI ldapns_Release(IADs *iface)
SysFreeString(ldap->host);
SysFreeString(ldap->object);
free_attributes(ldap);
+ free_attribute_types(ldap->at, ldap->at_count);
heap_free(ldap);
}
@@ -937,7 +940,8 @@ static HRESULT WINAPI openobj_OpenDSObject(IADsOpenDSObject *iface, BSTR path, B
IADs *ads;
LDAP *ld = NULL;
HRESULT hr;
- ULONG err;
+ ULONG err, at_count = 0;
+ struct attribute_type *at = NULL;
TRACE("%p,%s,%s,%p,%08x,%p\n", iface, debugstr_w(path), debugstr_w(user), password, flags, obj);
@@ -1035,6 +1039,8 @@ static HRESULT WINAPI openobj_OpenDSObject(IADsOpenDSObject *iface, BSTR path, B
goto fail;
}
}
+
+ at = load_schema(ld, &at_count);
}
hr = LDAPNamespace_create(&IID_IADs, (void **)&ads);
@@ -1045,6 +1051,8 @@ static HRESULT WINAPI openobj_OpenDSObject(IADsOpenDSObject *iface, BSTR path, B
ldap->host = host;
ldap->port = port;
ldap->object = object;
+ ldap->at = at;
+ ldap->at_count = at_count;
hr = IADs_QueryInterface(ads, &IID_IDispatch, (void **)obj);
IADs_Release(ads);
return hr;
@@ -1306,28 +1314,58 @@ static HRESULT WINAPI search_GetNextColumnName(IDirectorySearch *iface, ADS_SEAR
return S_ADS_NOMORE_COLUMNS;
}
-static HRESULT add_column_values(ADS_SEARCH_COLUMN *col, struct berval **values, DWORD count)
+static HRESULT add_column_values(LDAP_namespace *ldap, ADS_SEARCH_COLUMN *col,
+ const WCHAR *name, struct berval **values, DWORD count)
{
+ ADSTYPEENUM type;
DWORD i;
+ type = get_schema_type(name, ldap->at, ldap->at_count);
+
col->pADsValues = heap_alloc(count * sizeof(col->pADsValues[0]));
if (!col->pADsValues)
return E_OUTOFMEMORY;
for (i = 0; i < count; i++)
{
- DWORD outlen;
- TRACE("=> %s\n", debugstr_an(values[i]->bv_val, values[i]->bv_len));
- col->pADsValues[i].u.CaseIgnoreString = strnAtoW(values[i]->bv_val, values[i]->bv_len, &outlen);
- if (!col->pADsValues[i].u.CaseIgnoreString)
+ switch (type)
{
- heap_free(col->pADsValues);
- return E_OUTOFMEMORY;
+ default:
+ FIXME("no special handling for type %d\n", type);
+ /* fall through */
+ case ADSTYPE_DN_STRING:
+ case ADSTYPE_CASE_EXACT_STRING:
+ case ADSTYPE_CASE_IGNORE_STRING:
+ case ADSTYPE_PRINTABLE_STRING:
+ case ADSTYPE_NT_SECURITY_DESCRIPTOR:
+ {
+ DWORD outlen;
+ TRACE("=> %s\n", debugstr_an(values[i]->bv_val, values[i]->bv_len));
+ col->pADsValues[i].u.CaseIgnoreString = strnUtoW(values[i]->bv_val, values[i]->bv_len, &outlen);
+ if (!col->pADsValues[i].u.CaseIgnoreString)
+ {
+ heap_free(col->pADsValues);
+ return E_OUTOFMEMORY;
+ }
+ break;
+ }
+
+ case ADSTYPE_INTEGER:
+ col->pADsValues[i].u.Integer = strtol(values[i]->bv_val, NULL, 10);
+ TRACE("%s => %d\n", debugstr_an(values[i]->bv_val, values[i]->bv_len), col->pADsValues[i].u.Integer);
+ break;
+
+ case ADSTYPE_OCTET_STRING:
+ TRACE("=> %s\n", debugstr_an(values[i]->bv_val, values[i]->bv_len));
+ col->pADsValues[i].u.OctetString.dwLength = values[i]->bv_len;
+ col->pADsValues[i].u.OctetString.lpValue = (BYTE *)values[i]->bv_val;
+ break;
}
}
- col->dwADsType = ADSTYPE_CASE_IGNORE_STRING;
+ col->dwADsType = type;
col->dwNumValues = count;
+ col->pszAttrName = strdupW(name);
return S_OK;
}
@@ -1388,10 +1426,8 @@ exit:
count = ldap_count_values_len(values);
- hr = add_column_values(col, values, count);
+ hr = add_column_values(ldap, col, name, values, count);
ldap_value_free_len(values);
- if (hr == S_OK)
- col->pszAttrName = strdupW(name);
return hr;
}
@@ -1451,6 +1487,8 @@ static HRESULT LDAPNamespace_create(REFIID riid, void **obj)
ldap->attrs_count_allocated = 0;
ldap->attrs = NULL;
ldap->search.scope = ADS_SCOPE_SUBTREE;
+ ldap->at = NULL;
+ ldap->at_count = 0;
hr = IADs_QueryInterface(&ldap->IADs_iface, riid, obj);
IADs_Release(&ldap->IADs_iface);
diff --git a/dlls/adsldp/adsldp_private.h b/dlls/adsldp/adsldp_private.h
index 149d52eb63..bf24de2385 100644
--- a/dlls/adsldp/adsldp_private.h
+++ b/dlls/adsldp/adsldp_private.h
@@ -29,16 +29,16 @@ static inline WCHAR *strdupW(const WCHAR *src)
return dst;
}
-static inline LPWSTR strnAtoW( LPCSTR str, DWORD inlen, DWORD *outlen )
+static inline LPWSTR strnUtoW( LPCSTR str, DWORD inlen, DWORD *outlen )
{
LPWSTR ret = NULL;
*outlen = 0;
if (str)
{
- DWORD len = MultiByteToWideChar( CP_ACP, 0, str, inlen, NULL, 0 );
+ DWORD len = MultiByteToWideChar( CP_UTF8, 0, str, inlen, NULL, 0 );
if ((ret = heap_alloc( (len + 1) * sizeof(WCHAR) )))
{
- MultiByteToWideChar( CP_ACP, 0, str, inlen, ret, len );
+ MultiByteToWideChar( CP_UTF8, 0, str, inlen, ret, len );
ret[len] = 0;
*outlen = len;
}
@@ -46,6 +46,17 @@ static inline LPWSTR strnAtoW( LPCSTR str, DWORD inlen, DWORD *outlen )
return ret;
}
+struct attribute_type
+{
+ WCHAR *oid;
+ WCHAR *name;
+ WCHAR *syntax;
+ int single_value;
+};
+
DWORD map_ldap_error(DWORD) DECLSPEC_HIDDEN;
+struct attribute_type *load_schema(LDAP *ld, ULONG *) DECLSPEC_HIDDEN;
+ADSTYPEENUM get_schema_type(const WCHAR *, const struct attribute_type *, ULONG) DECLSPEC_HIDDEN;
+void free_attribute_types(struct attribute_type *, ULONG) DECLSPEC_HIDDEN;
#endif
diff --git a/dlls/adsldp/schema.c b/dlls/adsldp/schema.c
new file mode 100644
index 0000000000..ceced6bb64
--- /dev/null
+++ b/dlls/adsldp/schema.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2020 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "iads.h"
+#include "winldap.h"
+
+#include "adsldp_private.h"
+
+#include "wine/heap.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(adsldp);
+
+static const struct attribute_type *find_schema_type(const WCHAR *name, const struct attribute_type *at, ULONG count)
+{
+ ULONG i;
+
+ for (i = 0; i < count; i++)
+ if (at[i].name && !wcsicmp(at[i].name, name)) return &at[i];
+
+ return NULL;
+}
+
+/* RFC 4517 */
+ADSTYPEENUM get_schema_type(const WCHAR *name, const struct attribute_type *at, ULONG at_count)
+{
+ const struct attribute_type *type;
+
+ type = find_schema_type(name, at, at_count);
+ if (!type || !type->syntax) return ADSTYPE_CASE_IGNORE_STRING;
+
+ if (!wcscmp(type->syntax, L"1.3.6.1.4.1.1466.115.121.1.12"))
+ return ADSTYPE_DN_STRING;
+ if (!wcscmp(type->syntax, L"1.3.6.1.4.1.1466.115.121.1.15"))
+ return ADSTYPE_NT_SECURITY_DESCRIPTOR;
+ if (!wcscmp(type->syntax, L"1.3.6.1.4.1.1466.115.121.1.27"))
+ return ADSTYPE_INTEGER;
+ if (!wcscmp(type->syntax, L"1.3.6.1.4.1.1466.115.121.1.40"))
+ return ADSTYPE_OCTET_STRING;
+
+ FIXME("not handled type syntax %s for %s\n", debugstr_w(type->syntax), debugstr_w(name));
+ return ADSTYPE_CASE_IGNORE_STRING;
+}
+
+static void free_attribute_type(struct attribute_type *at)
+{
+ heap_free(at->oid);
+ heap_free(at->name);
+ heap_free(at->syntax);
+}
+
+void free_attribute_types(struct attribute_type *at, ULONG count)
+{
+ ULONG i;
+
+ for (i = 0; i < count; i++)
+ free_attribute_type(&at[i]);
+}
+
+static BOOL is_space(WCHAR c)
+{
+ return c == ' ' || c == '\t' || c == '\r' || c == '\n';
+}
+
+static WCHAR *parse_oid(WCHAR **str)
+{
+ WCHAR *oid, *p = *str, *end;
+ int count;
+
+ while (is_space(*p)) p++;
+
+ if (*p == '\'')
+ {
+ p++;
+ end = wcschr(p, '\'');
+ if (!end) return NULL;
+ }
+ else
+ {
+ end = p;
+ while (!is_space(*end)) end++;
+ }
+
+ count = end - p;
+ oid = heap_alloc((count + 1) * sizeof(WCHAR));
+ if (!oid) return NULL;
+
+ memcpy(oid, p, count * sizeof(WCHAR));
+ oid[count] = 0;
+
+ *str = end + 1;
+
+ return oid;
+}
+
+static WCHAR *parse_name(WCHAR **str)
+{
+ WCHAR *name, *p = *str, *end;
+ int count;
+
+ while (is_space(*p)) p++;
+
+ if (*p != '\'')
+ {
+ FIXME("not suported NAME start at %s\n", debugstr_w(p));
+ return NULL;
+ }
+
+ p++;
+ end = wcschr(p, '\'');
+ if (!end) return NULL;
+
+ count = end - p;
+ name = heap_alloc((count + 1) * sizeof(WCHAR));
+ if (!name) return NULL;
+
+ memcpy(name, p, count * sizeof(WCHAR));
+ name[count] = 0;
+
+ *str = end + 1;
+
+ return name;
+}
+
+static BOOL parse_attribute_type(WCHAR *str, struct attribute_type *at)
+{
+ WCHAR *p = str;
+
+ at->oid = NULL;
+ at->name = NULL;
+ at->syntax = NULL;
+ at->single_value = 0;
+
+ while (is_space(*p)) p++;
+ if (*p++ != '(') return FALSE;
+
+ at->oid = parse_oid(&p);
+ if (!at->oid) return FALSE;
+
+ while (*p)
+ {
+ while (is_space(*p)) p++;
+ if (*p == ')') break;
+
+ if (!wcsnicmp(p, L"NAME", 4))
+ {
+ p += 4;
+ at->name = parse_name(&p);
+ }
+ else if (!wcsnicmp(p, L"SYNTAX", 6))
+ {
+ p += 6;
+ at->syntax = parse_oid(&p);
+ }
+ else if (!wcsnicmp(p, L"SINGLE-VALUE", 12))
+ {
+ p += 12;
+ at->single_value = 1;
+ }
+ else if (!wcsnicmp(p, L"NO-USER-MODIFICATION", 20))
+ {
+ p += 20;
+ }
+ else
+ {
+ FIXME("not supported token at %s\n", debugstr_w(p));
+ free_attribute_type(at);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+struct attribute_type *load_schema(LDAP *ld, ULONG *at_count)
+{
+ WCHAR *subschema[] = { (WCHAR *)L"subschemaSubentry", NULL };
+ WCHAR *attribute_types[] = { (WCHAR *)L"attributeTypes", NULL };
+ ULONG err, count, i;
+ LDAPMessage *res, *entry;
+ WCHAR **schema = NULL;
+ struct attribute_type *at = NULL;
+
+ *at_count = 0;
+
+ err = ldap_search_sW(ld, NULL, LDAP_SCOPE_BASE, (WCHAR *)L"(objectClass=*)", subschema, FALSE, &res);
+ if (err != LDAP_SUCCESS)
+ {
+ TRACE("ldap_search_sW error %#x\n", err);
+ return NULL;
+ }
+
+ entry = ldap_first_entry(ld, res);
+ if (entry)
+ schema = ldap_get_valuesW(ld, entry, subschema[0]);
+
+ ldap_msgfree(res);
+ if (!schema) return NULL;
+
+ err = ldap_search_sW(ld, schema[0], LDAP_SCOPE_BASE, (WCHAR *)L"(objectClass=*)", attribute_types, FALSE, &res);
+ if (err != LDAP_SUCCESS)
+ {
+ TRACE("ldap_search_sW error %#x\n", err);
+ ldap_value_freeW(schema);
+ return NULL;
+ }
+
+ entry = ldap_first_entry(ld, res);
+ if (entry)
+ {
+ WCHAR **types;
+
+ types = ldap_get_valuesW(ld, entry, attribute_types[0]);
+ if (types)
+ {
+ count = ldap_count_valuesW(types);
+
+ at = heap_alloc(count * sizeof(*at));
+ if (!at) goto exit;
+
+ for (i = 0; i < count; i++)
+ {
+ TRACE("%s\n", debugstr_w(types[i]));
+
+ if (!parse_attribute_type(types[i], &at[i]))
+ {
+ WARN("parse_attribute_type failed\n");
+ continue;
+ }
+
+ TRACE("oid %s, name %s, syntax %s, single-value %d\n", debugstr_w(at[i].oid),
+ debugstr_w(at[i].name), debugstr_w(at[i].syntax), at[i].single_value);
+ }
+
+ ldap_value_freeW(types);
+ }
+ }
+
+exit:
+ ldap_msgfree(res);
+ if (at) *at_count = count;
+
+ return at;
+}
--
2.25.1