[PATCH 2/2] adsldp: Add initial version of attribute schema parser.

Dmitry Timoshkov dmitry at baikal.ru
Fri Mar 27 03:39:03 CDT 2020


Signed-off-by: Dmitry Timoshkov <dmitry at 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




More information about the wine-devel mailing list