pyldb: Split text/byte strings for compatibility with Python 3
authorPetr Viktorin <pviktori@redhat.com>
Tue, 9 Jun 2015 15:44:40 +0000 (17:44 +0200)
committerStefan Metzmacher <metze@samba.org>
Wed, 4 Nov 2015 12:12:00 +0000 (13:12 +0100)
Compatibility with Python 2, and backwards compatibility on Python 2,
is kept.

Under Python 3, DNs, attribute names, filters, controls are always text
(unicode) strings, encoded to/from UTF-8 for storage.
Attribute values are byte strings.

When creating DNs and attribute values, both text and bytes are accepted.
This allows creating messages from homogeneous dicts.

LDB Messages and MessageElements have a .text attribute, which offers
a text view on the contents: any value retrieved from it will be a text
string. The wrapper is implemented in a new Python module.

Thanks to Stefan Metzmacher for const warning fixes

Signed-off-by: Petr Viktorin <pviktori@redhat.com>
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
lib/ldb/_ldb_text.py [new file with mode: 0644]
lib/ldb/pyldb.c
lib/ldb/pyldb_util.c
lib/ldb/wscript

diff --git a/lib/ldb/_ldb_text.py b/lib/ldb/_ldb_text.py
new file mode 100644 (file)
index 0000000..f6f1ac0
--- /dev/null
@@ -0,0 +1,148 @@
+# Text wrapper for ldb bindings
+#
+# Copyright (C) 2015 Petr Viktorin <pviktori@redhat.com>
+# Published under the GNU LGPLv3 or later
+
+import sys
+import functools
+
+import ldb
+
+
+def _recursive_encode(obj):
+    if isinstance(obj, bytes):
+        return obj
+    elif isinstance(obj, str):
+        return obj.encode('utf-8')
+    else:
+        return [_recursive_encode(o) for o in obj]
+
+
+class _WrapBase(object):
+
+    @classmethod
+    def _wrap(cls, wrapped):
+        self = cls.__new__(cls)
+        self._wrapped = wrapped
+        return self
+
+    def __len__(self):
+        return len(self._wrapped)
+
+    def __eq__(self, other):
+        if hasattr(other, '_wrapped'):
+            return self._wrapped == other._wrapped
+        else:
+            return self._wrapped == other
+
+    def __ne__(self, other):
+        if hasattr(other, '_wrapped'):
+            return self._wrapped != other._wrapped
+        else:
+            return self._wrapped != other
+
+    def __lt__(self, other):
+        if hasattr(other, '_wrapped'):
+            return self._wrapped < other._wrapped
+        else:
+            return self._wrapped < other
+
+    def __le__(self, other):
+        if hasattr(other, '_wrapped'):
+            return self._wrapped >= other._wrapped
+        else:
+            return self._wrapped >= other
+
+    def __gt__(self, other):
+        if hasattr(other, '_wrapped'):
+            return self._wrapped > other._wrapped
+        else:
+            return self._wrapped > other
+
+    def __ge__(self, other):
+        if hasattr(other, '_wrapped'):
+            return self._wrapped >= other._wrapped
+        else:
+            return self._wrapped >= other
+
+    def __repr__(self):
+        return '%s.text' % repr(self._wrapped)
+
+
+class MessageElementTextWrapper(_WrapBase):
+
+    """Text interface for a LDB message element"""
+
+    def __iter__(self):
+        for item in self._wrapped:
+            yield item.decode('utf-8')
+
+    def __getitem__(self, key):
+        result = self._wrapped[key]
+        if result is None:
+            return None
+        else:
+            return result.decode('utf-8')
+
+    @property
+    def flags(self):
+        return self._wrapped.flags
+
+    @property
+    def set_flags(self):
+        return self._wrapped.set_flags
+
+_wrap_element = MessageElementTextWrapper._wrap
+
+
+class MessageTextWrapper(_WrapBase):
+
+    """Text interface for a LDB message"""
+
+    def __getitem__(self, key):
+        result = self._wrapped[key]
+        if result is None:
+            return None
+        else:
+            return _wrap_element(result)
+
+    def get(self, *args, **kwargs):
+        result = self._wrapped.get(*args, **kwargs)
+        if isinstance(result, ldb.MessageElement):
+            return _wrap_element(result)
+        elif isinstance(result, bytes):
+            return result.decode('utf-8')
+        else:
+            return result
+
+    def __setitem__(self, key, item):
+        self._wrapped[key] = _recursive_encode(item)
+
+    def __delitem__(self, key):
+        del self._wrapped[key]
+
+    def elements(self):
+        return [_wrap_element(el) for el in self._wrapped.elements()]
+
+    def items(self):
+        return [(attr, _wrap_element(el)) for attr, el in self._wrapped.items()]
+
+    @property
+    def keys(self):
+        return self._wrapped.keys
+
+    @property
+    def remove(self):
+        return self._wrapped.remove
+
+    @property
+    def add(self):
+        return self._wrapped.add
+
+    @property
+    def dn(self):
+        return self._wrapped.dn
+
+    @dn.setter
+    def dn(self, new_value):
+        self._wrapped.dn = new_value
index fb00e3581c28b1a52d04b69917b4b40f777f3a2d..8ff89e337e50d8a2ec37395b9831d49753df267b 100644 (file)
@@ -57,15 +57,34 @@ static struct ldb_message_element *PyObject_AsMessageElement(
                                                      unsigned int flags,
                                                      const char *attr_name);
 
-/* There's no Py_ssize_t in 2.4, apparently */
-#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 5
-typedef int Py_ssize_t;
-typedef inquiry lenfunc;
-typedef intargfunc ssizeargfunc;
-#endif
-
-
+#if PY_MAJOR_VERSION >= 3
+#define PyStr_Check PyUnicode_Check
+#define PyStr_FromString PyUnicode_FromString
+#define PyStr_FromStringAndSize PyUnicode_FromStringAndSize
+#define PyStr_FromFormat PyUnicode_FromFormat
+#define PyStr_FromFormatV PyUnicode_FromFormatV
+#define PyStr_AsUTF8 PyUnicode_AsUTF8
+#define PyStr_AsUTF8AndSize PyUnicode_AsUTF8AndSize
+#define PyInt_FromLong PyLong_FromLong
+#else
+#define PyStr_Check PyString_Check
+#define PyStr_FromString PyString_FromString
+#define PyStr_FromStringAndSize PyString_FromStringAndSize
+#define PyStr_FromFormat PyString_FromFormat
+#define PyStr_FromFormatV PyString_FromFormatV
+#define PyStr_AsUTF8 PyString_AsString
 
+const char *PyStr_AsUTF8AndSize(PyObject *pystr, Py_ssize_t *sizeptr);
+const char *
+PyStr_AsUTF8AndSize(PyObject *pystr, Py_ssize_t *sizeptr)
+{
+       const char * ret = PyString_AsString(pystr);
+       if (ret == NULL)
+               return NULL;
+       *sizeptr = PyString_Size(pystr);
+       return ret;
+}
+#endif
 
 static PyObject *richcmp(int cmp_val, int op)
 {
@@ -93,9 +112,9 @@ static PyObject *py_ldb_control_str(PyLdbControlObject *self)
                        PyErr_NoMemory();
                        return NULL;
                }
-               return PyString_FromString(control);
+               return PyStr_FromString(control);
        } else {
-               return PyString_FromFormat("ldb control");
+               return PyStr_FromString("ldb control");
        }
 }
 
@@ -108,9 +127,32 @@ static void py_ldb_control_dealloc(PyLdbControlObject *self)
        Py_TYPE(self)->tp_free(self);
 }
 
+/* Create a text (rather than bytes) interface for a LDB result object */
+static PyObject *wrap_text(const char *type, PyObject *wrapped)
+{
+       PyObject *mod, *cls, *constructor, *inst;
+       mod = PyImport_ImportModule("_ldb_text");
+       if (mod == NULL)
+               return NULL;
+       cls = PyObject_GetAttrString(mod, type);
+       Py_DECREF(mod);
+       if (cls == NULL) {
+               Py_DECREF(mod);
+               return NULL;
+       }
+       constructor = PyObject_GetAttrString(cls, "_wrap");
+       Py_DECREF(cls);
+       if (constructor == NULL) {
+               return NULL;
+       }
+       inst = PyObject_CallFunction(constructor, discard_const_p(char, "O"), wrapped);
+       Py_DECREF(constructor);
+       return inst;
+}
+
 static PyObject *py_ldb_control_get_oid(PyLdbControlObject *self)
 {
-       return PyString_FromString(self->data->oid);
+       return PyStr_FromString(self->data->oid);
 }
 
 static PyObject *py_ldb_control_get_critical(PyLdbControlObject *self)
@@ -208,7 +250,7 @@ static void PyErr_SetLdbError(PyObject *error, int ret, struct ldb_context *ldb_
 
 static PyObject *PyObject_FromLdbValue(const struct ldb_val *val)
 {
-       return PyString_FromStringAndSize((const char *)val->data, val->length);
+       return PyBytes_FromStringAndSize((const char *)val->data, val->length);
 }
 
 /**
@@ -334,7 +376,7 @@ static PyObject *PyLdbResult_FromResult(struct ldb_result *result)
        }
 
        for (i = 0;result->refs && result->refs[i]; i++) {
-               PyList_SetItem(referals, i, PyString_FromString(result->refs[i]));
+               PyList_SetItem(referals, i, PyStr_FromString(result->refs[i]));
        }
        ret->referals = referals;
        return (PyObject *)ret;
@@ -392,22 +434,22 @@ static PyObject *py_ldb_dn_is_null(PyLdbDnObject *self)
  
 static PyObject *py_ldb_dn_get_casefold(PyLdbDnObject *self)
 {
-       return PyString_FromString(ldb_dn_get_casefold(self->dn));
+       return PyStr_FromString(ldb_dn_get_casefold(self->dn));
 }
 
 static PyObject *py_ldb_dn_get_linearized(PyLdbDnObject *self)
 {
-       return PyString_FromString(ldb_dn_get_linearized(self->dn));
+       return PyStr_FromString(ldb_dn_get_linearized(self->dn));
 }
 
 static PyObject *py_ldb_dn_canonical_str(PyLdbDnObject *self)
 {
-       return PyString_FromString(ldb_dn_canonical_string(self->dn, self->dn));
+       return PyStr_FromString(ldb_dn_canonical_string(self->dn, self->dn));
 }
 
 static PyObject *py_ldb_dn_canonical_ex_str(PyLdbDnObject *self)
 {
-       return PyString_FromString(ldb_dn_canonical_ex_string(self->dn, self->dn));
+       return PyStr_FromString(ldb_dn_canonical_ex_string(self->dn, self->dn));
 }
 
 static PyObject *py_ldb_dn_extended_str(PyLdbDnObject *self, PyObject *args, PyObject *kwargs)
@@ -418,7 +460,7 @@ static PyObject *py_ldb_dn_extended_str(PyLdbDnObject *self, PyObject *args, PyO
                                         discard_const_p(char *, kwnames),
                                         &mode))
                return NULL;
-       return PyString_FromString(ldb_dn_get_extended_linearized(self->dn, self->dn, mode));
+       return PyStr_FromString(ldb_dn_get_extended_linearized(self->dn, self->dn, mode));
 }
 
 static PyObject *py_ldb_dn_get_extended_component(PyLdbDnObject *self, PyObject *args)
@@ -433,14 +475,15 @@ static PyObject *py_ldb_dn_get_extended_component(PyLdbDnObject *self, PyObject
                Py_RETURN_NONE;
        }
 
-       return PyString_FromStringAndSize((const char *)val->data, val->length);
+       return PyBytes_FromStringAndSize((const char *)val->data, val->length);
 }
 
 static PyObject *py_ldb_dn_set_extended_component(PyLdbDnObject *self, PyObject *args)
 {
        char *name;
        PyObject *value;
-       int err;
+       int err, result;
+       Py_ssize_t size;
 
        if (!PyArg_ParseTuple(args, "sO", &name, &value))
                return NULL;
@@ -449,12 +492,12 @@ static PyObject *py_ldb_dn_set_extended_component(PyLdbDnObject *self, PyObject
                err = ldb_dn_set_extended_component(self->dn, name, NULL);
        } else {
                struct ldb_val val;
-               if (!PyString_Check(value)) {
-                       PyErr_SetString(PyExc_TypeError, "Expected a string argument");
+               result = PyBytes_AsStringAndSize(value, (char **) &val.data, &size);
+               val.length = size;
+               if (result != 0) {
+                       PyErr_SetString(PyExc_TypeError, "Expected a bytestring argument");
                        return NULL;
                }
-               val.data = (uint8_t *)PyString_AsString(value);
-               val.length = PyString_Size(value);
                err = ldb_dn_set_extended_component(self->dn, name, &val);
        }
 
@@ -468,7 +511,19 @@ static PyObject *py_ldb_dn_set_extended_component(PyLdbDnObject *self, PyObject
 
 static PyObject *py_ldb_dn_repr(PyLdbDnObject *self)
 {
-       return PyString_FromFormat("Dn(%s)", PyObject_REPR(PyString_FromString(ldb_dn_get_linearized(self->dn))));
+       PyObject *str = PyStr_FromString(ldb_dn_get_linearized(self->dn));
+       PyObject *repr, *result;
+       if (str == NULL)
+               return NULL;
+       repr = PyObject_Repr(str);
+       if (repr == NULL) {
+               Py_DECREF(str);
+               return NULL;
+       }
+       result = PyStr_FromFormat("Dn(%s)", PyStr_AsUTF8(repr));
+       Py_DECREF(str);
+       Py_DECREF(repr);
+       return result;
 }
 
 static PyObject *py_ldb_dn_check_special(PyLdbDnObject *self, PyObject *args)
@@ -589,7 +644,7 @@ static PyObject *py_ldb_dn_get_component_name(PyLdbDnObject *self, PyObject *arg
                Py_RETURN_NONE;
        }
 
-       return PyString_FromString(name);
+       return PyStr_FromString(name);
 }
 
 static PyObject *py_ldb_dn_get_component_value(PyLdbDnObject *self, PyObject *args)
@@ -617,18 +672,19 @@ static PyObject *py_ldb_dn_set_component(PyLdbDnObject *self, PyObject *args)
        char *name = NULL;
        PyObject *value = Py_None;
        struct ldb_val val = { NULL, };
-       int err;
+       int err, ret;
+       Py_ssize_t size;
 
        if (!PyArg_ParseTuple(args, "IsO", &num, &name, &value))
                return NULL;
 
        if (value != Py_None) {
-               if (!PyString_Check(value)) {
-                       PyErr_SetString(PyExc_TypeError, "Expected a string argument");
+               ret = PyBytes_AsStringAndSize(value, (char **) &val.data, &size);
+               if (ret != 0) {
+                       PyErr_SetString(PyExc_TypeError, "Expected a bytestring argument");
                        return NULL;
                }
-               val.data = (uint8_t *)PyString_AsString(value);
-               val.length = PyString_Size(value);
+               val.length = size;
        }
 
        err = ldb_dn_set_component(self->dn, num, name, val);
@@ -652,7 +708,7 @@ static PyObject *py_ldb_dn_get_rdn_name(PyLdbDnObject *self)
                Py_RETURN_NONE;
        }
 
-       return PyString_FromString(name);
+       return PyStr_FromString(name);
 }
 
 static PyObject *py_ldb_dn_get_rdn_value(PyLdbDnObject *self)
@@ -853,7 +909,7 @@ static void py_ldb_debug(void *context, enum ldb_debug_level level, const char *
 static void py_ldb_debug(void *context, enum ldb_debug_level level, const char *fmt, va_list ap)
 {
        PyObject *fn = (PyObject *)context;
-       PyObject_CallFunction(fn, discard_const_p(char, "(i,O)"), level, PyString_FromFormatV(fmt, ap));
+       PyObject_CallFunction(fn, discard_const_p(char, "(i,O)"), level, PyStr_FromFormatV(fmt, ap));
 }
 
 static PyObject *py_ldb_debug_func;
@@ -950,7 +1006,7 @@ static PyObject *py_ldb_setup_wellknown_attributes(PyLdbObject *self)
 
 static PyObject *py_ldb_repr(PyLdbObject *self)
 {
-       return PyString_FromFormat("<ldb connection>");
+       return PyStr_FromString("<ldb connection>");
 }
 
 static PyObject *py_ldb_get_root_basedn(PyLdbObject *self)
@@ -986,8 +1042,8 @@ static PyObject *py_ldb_get_default_basedn(PyLdbObject *self)
        return py_ldb_dn_copy(dn);
 }
 
-static const char **PyList_AsStringList(TALLOC_CTX *mem_ctx, PyObject *list, 
-                                       const char *paramname)
+static const char **PyList_AsStrList(TALLOC_CTX *mem_ctx, PyObject *list,
+                    const char *paramname)
 {
        const char **ret;
        Py_ssize_t i;
@@ -1002,13 +1058,20 @@ static const char **PyList_AsStringList(TALLOC_CTX *mem_ctx, PyObject *list,
        }
 
        for (i = 0; i < PyList_Size(list); i++) {
+               const char *str = NULL;
+               Py_ssize_t size;
                PyObject *item = PyList_GetItem(list, i);
-               if (!PyString_Check(item)) {
+               if (!PyStr_Check(item)) {
                        PyErr_Format(PyExc_TypeError, "%s should be strings", paramname);
+                       talloc_free(ret);
                        return NULL;
                }
-               ret[i] = talloc_strndup(ret, PyString_AsString(item),
-                                       PyString_Size(item));
+               str = PyStr_AsUTF8AndSize(item, &size);
+               if (str == NULL) {
+                       talloc_free(ret);
+                       return NULL;
+               }
+               ret[i] = talloc_strndup(ret, str, size);
        }
        ret[i] = NULL;
        return ret;
@@ -1034,7 +1097,7 @@ static int py_ldb_init(PyLdbObject *self, PyObject *args, PyObject *kwargs)
        if (py_options == Py_None) {
                options = NULL;
        } else {
-               options = PyList_AsStringList(ldb, py_options, "options");
+               options = PyList_AsStrList(ldb, py_options, "options");
                if (options == NULL)
                        return -1;
        }
@@ -1090,7 +1153,7 @@ static PyObject *py_ldb_connect(PyLdbObject *self, PyObject *args, PyObject *kwa
        if (py_options == Py_None) {
                options = NULL;
        } else {
-               options = PyList_AsStringList(NULL, py_options, "options");
+               options = PyList_AsStrList(NULL, py_options, "options");
                if (options == NULL)
                        return NULL;
        }
@@ -1132,7 +1195,7 @@ static PyObject *py_ldb_modify(PyLdbObject *self, PyObject *args, PyObject *kwar
        if (py_controls == Py_None) {
                parsed_controls = NULL;
        } else {
-               const char **controls = PyList_AsStringList(mem_ctx, py_controls, "controls");
+               const char **controls = PyList_AsStrList(mem_ctx, py_controls, "controls");
                if (controls == NULL) {
                        talloc_free(mem_ctx);
                        return NULL;
@@ -1159,7 +1222,7 @@ static PyObject *py_ldb_modify(PyLdbObject *self, PyObject *args, PyObject *kwar
 
        ret = ldb_build_mod_req(&req, ldb_ctx, mem_ctx, msg, parsed_controls,
                                NULL, ldb_op_default_callback, NULL);
-        if (ret != LDB_SUCCESS) {
+       if (ret != LDB_SUCCESS) {
                PyErr_SetString(PyExc_TypeError, "failed to build request");
                talloc_free(mem_ctx);
                return NULL;
@@ -1236,7 +1299,7 @@ static struct ldb_message *PyDict_AsMessage(TALLOC_CTX *mem_ctx,
        }
 
        while (PyDict_Next(py_obj, &dict_pos, &key, &value)) {
-               char *key_str = PyString_AsString(key);
+               char *key_str = PyStr_AsUTF8(key);
                if (ldb_attr_cmp(key_str, "dn") != 0) {
                        msg_el = PyObject_AsMessageElement(msg->elements, value,
                                                           mod_flags, key_str);
@@ -1281,7 +1344,7 @@ static PyObject *py_ldb_add(PyLdbObject *self, PyObject *args, PyObject *kwargs)
        if (py_controls == Py_None) {
                parsed_controls = NULL;
        } else {
-               const char **controls = PyList_AsStringList(mem_ctx, py_controls, "controls");
+               const char **controls = PyList_AsStrList(mem_ctx, py_controls, "controls");
                if (controls == NULL) {
                        talloc_free(mem_ctx);
                        return NULL;
@@ -1374,7 +1437,7 @@ static PyObject *py_ldb_delete(PyLdbObject *self, PyObject *args, PyObject *kwar
        if (py_controls == Py_None) {
                parsed_controls = NULL;
        } else {
-               const char **controls = PyList_AsStringList(mem_ctx, py_controls, "controls");
+               const char **controls = PyList_AsStrList(mem_ctx, py_controls, "controls");
                if (controls == NULL) {
                        talloc_free(mem_ctx);
                        return NULL;
@@ -1452,7 +1515,7 @@ static PyObject *py_ldb_rename(PyLdbObject *self, PyObject *args, PyObject *kwar
        if (py_controls == Py_None) {
                parsed_controls = NULL;
        } else {
-               const char **controls = PyList_AsStringList(mem_ctx, py_controls, "controls");
+               const char **controls = PyList_AsStrList(mem_ctx, py_controls, "controls");
                if (controls == NULL) {
                        talloc_free(mem_ctx);
                        return NULL;
@@ -1577,7 +1640,7 @@ static PyObject *py_ldb_write_ldif(PyLdbObject *self, PyObject *args)
                return NULL;
        }
 
-       ret = PyString_FromString(string);
+       ret = PyStr_FromString(string);
 
        talloc_free(mem_ctx);
 
@@ -1666,14 +1729,16 @@ static PyObject *py_ldb_schema_format_value(PyLdbObject *self, PyObject *args)
        PyObject *ret;
        char *element_name;
        PyObject *val;
+       Py_ssize_t size;
+       int result;
 
        if (!PyArg_ParseTuple(args, "sO", &element_name, &val))
                return NULL;
 
-       old_val.data = (uint8_t *)PyString_AsString(val);
-       old_val.length = PyString_Size(val);
+       result = PyBytes_AsStringAndSize(val, (char **)&old_val.data, &size);
+       old_val.length = size;
 
-       if (old_val.data == NULL) {
+       if (result != 0) {
                PyErr_SetString(PyExc_RuntimeError, "Failed to convert passed value to String");
                return NULL;
        }
@@ -1695,7 +1760,7 @@ static PyObject *py_ldb_schema_format_value(PyLdbObject *self, PyObject *args)
                Py_RETURN_NONE;
        }
 
-       ret = PyString_FromStringAndSize((const char *)new_val.data, new_val.length);
+       ret = PyBytes_FromStringAndSize((const char *)new_val.data, new_val.length);
 
        talloc_free(mem_ctx);
 
@@ -1737,7 +1802,7 @@ static PyObject *py_ldb_search(PyLdbObject *self, PyObject *args, PyObject *kwar
        if (py_attrs == Py_None) {
                attrs = NULL;
        } else {
-               attrs = PyList_AsStringList(mem_ctx, py_attrs, "attrs");
+               attrs = PyList_AsStrList(mem_ctx, py_attrs, "attrs");
                if (attrs == NULL) {
                        talloc_free(mem_ctx);
                        return NULL;
@@ -1756,7 +1821,7 @@ static PyObject *py_ldb_search(PyLdbObject *self, PyObject *args, PyObject *kwar
        if (py_controls == Py_None) {
                parsed_controls = NULL;
        } else {
-               const char **controls = PyList_AsStringList(mem_ctx, py_controls, "controls");
+               const char **controls = PyList_AsStrList(mem_ctx, py_controls, "controls");
                if (controls == NULL) {
                        talloc_free(mem_ctx);
                        return NULL;
@@ -2129,7 +2194,7 @@ static PySequenceMethods py_ldb_result_seq = {
 
 static PyObject *py_ldb_result_repr(PyLdbObject *self)
 {
-       return PyString_FromFormat("<ldb result>");
+       return PyStr_FromString("<ldb result>");
 }
 
 
@@ -2148,13 +2213,13 @@ static PyTypeObject PyLdbResult = {
 
 static PyObject *py_ldb_module_repr(PyLdbModuleObject *self)
 {
-       return PyString_FromFormat("<ldb module '%s'>",
+       return PyStr_FromFormat("<ldb module '%s'>",
                pyldb_Module_AsModule(self)->ops->name);
 }
 
 static PyObject *py_ldb_module_str(PyLdbModuleObject *self)
 {
-       return PyString_FromString(pyldb_Module_AsModule(self)->ops->name);
+       return PyStr_FromString(pyldb_Module_AsModule(self)->ops->name);
 }
 
 static PyObject *py_ldb_module_start_transaction(PyLdbModuleObject *self)
@@ -2195,7 +2260,7 @@ static PyObject *py_ldb_module_search(PyLdbModuleObject *self, PyObject *args, P
        if (py_attrs == Py_None) {
                attrs = NULL;
        } else {
-               attrs = PyList_AsStringList(NULL, py_attrs, "attrs");
+               attrs = PyList_AsStrList(NULL, py_attrs, "attrs");
                if (attrs == NULL)
                        return NULL;
        }
@@ -2359,6 +2424,9 @@ static struct ldb_message_element *PyObject_AsMessageElement(
                                                      const char *attr_name)
 {
        struct ldb_message_element *me;
+       const char *msg = NULL;
+       Py_ssize_t size;
+       int result;
 
        if (pyldb_MessageElement_Check(set_obj)) {
                PyLdbMessageElementObject *set_obj_as_me = (PyLdbMessageElementObject *)set_obj;
@@ -2378,28 +2446,58 @@ static struct ldb_message_element *PyObject_AsMessageElement(
 
        me->name = talloc_strdup(me, attr_name);
        me->flags = flags;
-       if (PyString_Check(set_obj)) {
+       if (PyBytes_Check(set_obj) || PyStr_Check(set_obj)) {
                me->num_values = 1;
                me->values = talloc_array(me, struct ldb_val, me->num_values);
-               me->values[0].length = PyString_Size(set_obj);
-               me->values[0].data = talloc_memdup(me, 
-                       (uint8_t *)PyString_AsString(set_obj), me->values[0].length+1);
+               if (PyBytes_Check(set_obj)) {
+                       char *_msg = NULL;
+                       result = PyBytes_AsStringAndSize(set_obj, &_msg, &size);
+                       if (result != 0) {
+                               talloc_free(me);
+                               return NULL;
+                       }
+                       msg = _msg;
+               } else {
+                       msg = PyStr_AsUTF8AndSize(set_obj, &size);
+                       if (msg == NULL) {
+                               talloc_free(me);
+                               return NULL;
+                       }
+               }
+               me->values[0].data = talloc_memdup(me,
+                                                  (const uint8_t *)msg,
+                                                  size+1);
+               me->values[0].length = size;
        } else if (PySequence_Check(set_obj)) {
                Py_ssize_t i;
                me->num_values = PySequence_Size(set_obj);
                me->values = talloc_array(me, struct ldb_val, me->num_values);
                for (i = 0; i < me->num_values; i++) {
                        PyObject *obj = PySequence_GetItem(set_obj, i);
-                       if (!PyString_Check(obj)) {
+                       if (PyBytes_Check(obj)) {
+                               char *_msg = NULL;
+                               result = PyBytes_AsStringAndSize(obj, &_msg, &size);
+                               if (result != 0) {
+                                       talloc_free(me);
+                                       return NULL;
+                               }
+                               msg = _msg;
+                       } else if (PyStr_Check(obj)) {
+                               msg = PyStr_AsUTF8AndSize(obj, &size);
+                               if (msg == NULL) {
+                                       talloc_free(me);
+                                       return NULL;
+                               }
+                       } else {
                                PyErr_Format(PyExc_TypeError,
                                             "Expected string as element %zd in list", i);
                                talloc_free(me);
                                return NULL;
                        }
-
-                       me->values[i].length = PyString_Size(obj);
-                       me->values[i].data = talloc_memdup(me, 
-                               (uint8_t *)PyString_AsString(obj), me->values[i].length+1);
+                       me->values[i].data = talloc_memdup(me,
+                                                          (const uint8_t *)msg,
+                                                          size+1);
+                       me->values[i].length = size;
                }
        } else {
                PyErr_Format(PyExc_TypeError,
@@ -2477,7 +2575,7 @@ static PyObject *py_ldb_msg_element_find(PyLdbMessageElementObject *self, Py_ssi
                PyErr_SetString(PyExc_IndexError, "Out of range");
                return NULL;
        }
-       return PyString_FromStringAndSize((char *)el->values[idx].data, el->values[idx].length);
+       return PyBytes_FromStringAndSize((char *)el->values[idx].data, el->values[idx].length);
 }
 
 static PySequenceMethods py_ldb_msg_element_seq = {
@@ -2532,6 +2630,9 @@ static PyObject *py_ldb_msg_element_new(PyTypeObject *type, PyObject *args, PyOb
        const char * const kwnames[] = { "elements", "flags", "name", NULL };
        PyLdbMessageElementObject *ret;
        TALLOC_CTX *mem_ctx;
+       const char *msg = NULL;
+       Py_ssize_t size;
+       int result;
 
        if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OIs",
                                         discard_const_p(char *, kwnames),
@@ -2553,7 +2654,8 @@ static PyObject *py_ldb_msg_element_new(PyTypeObject *type, PyObject *args, PyOb
 
        if (py_elements != NULL) {
                Py_ssize_t i;
-               if (PyString_Check(py_elements)) {
+               if (PyBytes_Check(py_elements)) {
+                       char *_msg = NULL;
                        el->num_values = 1;
                        el->values = talloc_array(el, struct ldb_val, 1);
                        if (el->values == NULL) {
@@ -2561,9 +2663,15 @@ static PyObject *py_ldb_msg_element_new(PyTypeObject *type, PyObject *args, PyOb
                                PyErr_NoMemory();
                                return NULL;
                        }
-                       el->values[0].length = PyString_Size(py_elements);
+                       result = PyBytes_AsStringAndSize(py_elements, &_msg, &size);
+                       if (result != 0) {
+                               talloc_free(mem_ctx);
+                               return NULL;
+                       }
+                       msg = _msg;
                        el->values[0].data = talloc_memdup(el->values, 
-                               (uint8_t *)PyString_AsString(py_elements), el->values[0].length+1);
+                               (const uint8_t *)msg, size);
+                       el->values[0].length = size;
                } else if (PySequence_Check(py_elements)) {
                        el->num_values = PySequence_Size(py_elements);
                        el->values = talloc_array(el, struct ldb_val, el->num_values);
@@ -2578,15 +2686,25 @@ static PyObject *py_ldb_msg_element_new(PyTypeObject *type, PyObject *args, PyOb
                                        talloc_free(mem_ctx);
                                        return NULL;
                                }
-                               if (!PyString_Check(item)) {
+                               if (PyBytes_Check(item)) {
+                                       char *_msg = NULL;
+                                       result = PyBytes_AsStringAndSize(item, &_msg, &size);
+                                       msg = _msg;
+                               } else if (PyStr_Check(item)) {
+                                       msg = PyStr_AsUTF8AndSize(item, &size);
+                                       result = (msg == NULL) ? -1 : 0;
+                               } else {
                                        PyErr_Format(PyExc_TypeError, 
                                                     "Expected string as element %zd in list", i);
+                                       result = -1;
+                               }
+                               if (result != 0) {
                                        talloc_free(mem_ctx);
                                        return NULL;
                                }
-                               el->values[i].length = PyString_Size(item);
                                el->values[i].data = talloc_memdup(el,
-                                       (uint8_t *)PyString_AsString(item), el->values[i].length+1);
+                                       (const uint8_t *)msg, size+1);
+                               el->values[i].length = size;
                        }
                } else {
                        PyErr_SetString(PyExc_TypeError, 
@@ -2621,17 +2739,17 @@ static PyObject *py_ldb_msg_element_repr(PyLdbMessageElementObject *self)
                PyObject *o = py_ldb_msg_element_find(self, i);
                repr = PyObject_Repr(o);
                if (element_str == NULL)
-                       element_str = talloc_strdup(NULL, PyString_AsString(repr));
+                       element_str = talloc_strdup(NULL, PyStr_AsUTF8(repr));
                else
-                       element_str = talloc_asprintf_append(element_str, ",%s", PyString_AsString(repr));
+                       element_str = talloc_asprintf_append(element_str, ",%s", PyStr_AsUTF8(repr));
                Py_DECREF(repr);
        }
 
        if (element_str != NULL) {
-               ret = PyString_FromFormat("MessageElement([%s])", element_str);
+               ret = PyStr_FromFormat("MessageElement([%s])", element_str);
                talloc_free(element_str);
        } else {
-               ret = PyString_FromString("MessageElement([])");
+               ret = PyStr_FromString("MessageElement([])");
        }
 
        return ret;
@@ -2642,7 +2760,7 @@ static PyObject *py_ldb_msg_element_str(PyLdbMessageElementObject *self)
        struct ldb_message_element *el = pyldb_MessageElement_AsMessageElement(self);
 
        if (el->num_values == 1)
-               return PyString_FromStringAndSize((char *)el->values[0].data, el->values[0].length);
+               return PyStr_FromStringAndSize((char *)el->values[0].data, el->values[0].length);
        else
                Py_RETURN_NONE;
 }
@@ -2653,6 +2771,16 @@ static void py_ldb_msg_element_dealloc(PyLdbMessageElementObject *self)
        PyObject_Del(self);
 }
 
+static PyObject *py_ldb_msg_element_get_text(PyObject *self, void *closure)
+{
+       return wrap_text("MessageElementTextWrapper", self);
+}
+
+static PyGetSetDef py_ldb_msg_element_getset[] = {
+       { discard_const_p(char, "text"), (getter)py_ldb_msg_element_get_text, NULL, NULL },
+       { NULL }
+};
+
 static PyTypeObject PyLdbMessageElement = {
        .tp_name = "ldb.MessageElement",
        .tp_basicsize = sizeof(PyLdbMessageElementObject),
@@ -2660,6 +2788,7 @@ static PyTypeObject PyLdbMessageElement = {
        .tp_repr = (reprfunc)py_ldb_msg_element_repr,
        .tp_str = (reprfunc)py_ldb_msg_element_str,
        .tp_methods = py_ldb_msg_element_methods,
+       .tp_getset = py_ldb_msg_element_getset,
        .tp_richcompare = (richcmpfunc)py_ldb_msg_element_richcmp,
        .tp_iter = (getiterfunc)py_ldb_msg_element_iter,
        .tp_as_sequence = &py_ldb_msg_element_seq,
@@ -2729,11 +2858,11 @@ static PyObject *py_ldb_msg_keys(PyLdbMessageObject *self)
        Py_ssize_t i, j = 0;
        PyObject *obj = PyList_New(msg->num_elements+(msg->dn != NULL?1:0));
        if (msg->dn != NULL) {
-               PyList_SetItem(obj, j, PyString_FromString("dn"));
+               PyList_SetItem(obj, j, PyStr_FromString("dn"));
                j++;
        }
        for (i = 0; i < msg->num_elements; i++) {
-               PyList_SetItem(obj, j, PyString_FromString(msg->elements[i].name));
+               PyList_SetItem(obj, j, PyStr_FromString(msg->elements[i].name));
                j++;
        }
        return obj;
@@ -2744,11 +2873,11 @@ static PyObject *py_ldb_msg_getitem_helper(PyLdbMessageObject *self, PyObject *p
        struct ldb_message_element *el;
        char *name;
        struct ldb_message *msg = pyldb_Message_AsMessage(self);
-       if (!PyString_Check(py_name)) {
+       name = PyStr_AsUTF8(py_name);
+       if (name == NULL) {
                PyErr_SetNone(PyExc_TypeError);
                return NULL;
        }
-       name = PyString_AsString(py_name);
        if (!ldb_attr_cmp(name, "dn"))
                return pyldb_Dn_FromDn(msg->dn);
        el = ldb_msg_find_element(msg, name);
@@ -2910,12 +3039,12 @@ static int py_ldb_msg_setitem(PyLdbMessageObject *self, PyObject *name, PyObject
 {
        char *attr_name;
 
-       if (!PyString_Check(name)) {
+       attr_name = PyStr_AsUTF8(name);
+       if (attr_name == NULL) {
                PyErr_SetNone(PyExc_TypeError);
                return -1;
        }
 
-       attr_name = PyString_AsString(name);
        if (value == NULL) {
                /* delitem */
                ldb_msg_remove_attr(self->msg, attr_name);
@@ -3026,8 +3155,14 @@ static int py_ldb_msg_set_dn(PyLdbMessageObject *self, PyObject *value, void *cl
        return 0;
 }
 
+static PyObject *py_ldb_msg_get_text(PyObject *self, void *closure)
+{
+       return wrap_text("MessageTextWrapper", self);
+}
+
 static PyGetSetDef py_ldb_msg_getset[] = {
        { discard_const_p(char, "dn"), (getter)py_ldb_msg_get_dn, (setter)py_ldb_msg_set_dn, NULL },
+       { discard_const_p(char, "text"), (getter)py_ldb_msg_get_text, NULL, NULL },
        { NULL }
 };
 
@@ -3041,7 +3176,7 @@ static PyObject *py_ldb_msg_repr(PyLdbMessageObject *self)
                Py_DECREF(dict);
                return NULL;
        }
-       ret = PyString_FromFormat("Message(%s)", PyString_AsString(repr));
+       ret = PyStr_FromFormat("Message(%s)", PyStr_AsUTF8(repr));
        Py_DECREF(repr);
        Py_DECREF(dict);
        return ret;
@@ -3164,7 +3299,7 @@ static int py_module_search(struct ldb_module *mod, struct ldb_request *req)
                for (len = 0; req->op.search.attrs[len]; len++);
                py_attrs = PyList_New(len);
                for (i = 0; i < len; i++)
-                       PyList_SetItem(py_attrs, i, PyString_FromString(req->op.search.attrs[i]));
+                       PyList_SetItem(py_attrs, i, PyStr_FromString(req->op.search.attrs[i]));
        }
 
        py_result = PyObject_CallMethod(py_ldb, discard_const_p(char, "search"),
@@ -3422,7 +3557,7 @@ static PyObject *py_register_module(PyObject *module, PyObject *args)
                return NULL;
        }
 
-       ops->name = talloc_strdup(ops, PyString_AsString(PyObject_GetAttrString(input, discard_const_p(char, "name"))));
+       ops->name = talloc_strdup(ops, PyStr_AsUTF8(PyObject_GetAttrString(input, discard_const_p(char, "name"))));
 
        Py_INCREF(input);
        ops->private_data = input;
@@ -3455,7 +3590,7 @@ static PyObject *py_timestring(PyObject *module, PyObject *args)
        if (!PyArg_ParseTuple(args, "l", &t_val))
                return NULL;
        tresult = ldb_timestring(NULL, (time_t) t_val);
-       ret = PyString_FromString(tresult);
+       ret = PyStr_FromString(tresult);
        talloc_free(tresult);
        return ret;
 }
@@ -3497,7 +3632,7 @@ static PyObject *py_binary_encode(PyObject *self, PyObject *args)
                PyErr_SetString(PyExc_TypeError, "unable to encode binary string");
                return NULL;
        }
-       ret = PyString_FromString(encoded);
+       ret = PyStr_FromString(encoded);
        talloc_free(encoded);
        return ret;
 }
@@ -3519,7 +3654,7 @@ static PyObject *py_binary_decode(PyObject *self, PyObject *args)
                PyErr_SetString(PyExc_TypeError, "unable to decode binary string");
                return NULL;
        }
-       ret = Py_BuildValue("s#", val.data, val.length);
+       ret = PyBytes_FromStringAndSize((const char*)val.data, val.length);
        talloc_free(val.data);
        return ret;
 }
index 4be9126405d92e2779097674358a1938b903b707..3bda1dbc2a86a77fe223310420feec68089d8ec5 100644 (file)
 
 static PyObject *ldb_module = NULL;
 
-/* There's no Py_ssize_t in 2.4, apparently */
-#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 5
-typedef int Py_ssize_t;
-typedef inquiry lenfunc;
-typedef intargfunc ssizeargfunc;
+#if PY_MAJOR_VERSION >= 3
+#define PyStr_Check PyUnicode_Check
+#define PyStr_AsUTF8 PyUnicode_AsUTF8
+#else
+#define PyStr_Check PyString_Check
+#define PyStr_AsUTF8 PyString_AsString
 #endif
 
 /**
@@ -69,8 +70,14 @@ bool pyldb_Object_AsDn(TALLOC_CTX *mem_ctx, PyObject *object,
        struct ldb_dn *odn;
        PyTypeObject *PyLdb_Dn_Type;
 
-       if (ldb_ctx != NULL && PyString_Check(object)) {
-               odn = ldb_dn_new(mem_ctx, ldb_ctx, PyString_AsString(object));
+       if (ldb_ctx != NULL && PyStr_Check(object)) {
+               odn = ldb_dn_new(mem_ctx, ldb_ctx, PyStr_AsUTF8(object));
+               *dn = odn;
+               return true;
+       }
+
+       if (ldb_ctx != NULL && PyBytes_Check(object)) {
+               odn = ldb_dn_new(mem_ctx, ldb_ctx, PyBytes_AsString(object));
                *dn = odn;
                return true;
        }
index 8322a56997b6821fde074cf592b26408eb543351..cb0e5a2a9c1832957a248430dafb0a376b8289ff 100755 (executable)
@@ -133,6 +133,14 @@ def build(bld):
                           abi_directory='ABI',
                           abi_match='pyldb_*')
 
+    if not bld.env.disable_python:
+        for env in bld.gen_python_environments(['PKGCONFIGDIR']):
+            bld.SAMBA_SCRIPT('_ldb_text.py',
+                             pattern='_ldb_text.py',
+                             installdir='python')
+
+            bld.INSTALL_FILES('${PYTHONARCHDIR}', '_ldb_text.py')
+
     if not bld.CONFIG_SET('USING_SYSTEM_LDB'):
         if Options.is_install:
             modules_dir = bld.EXPAND_VARIABLES('${LDB_MODULESDIR}')