ldb/python: Add bindings for schema functions.
[ira/wip.git] / source / lib / ldb / ldb.i
index 15a49ec9c5e2aacca110aad7d8a4ad7386eb1ca9..336100c4f0a8bbfad118e67f2016f89b98301c0b 100644 (file)
@@ -5,7 +5,7 @@
 
    Copyright (C) 2005,2006 Tim Potter <tpot@samba.org>
    Copyright (C) 2006 Simo Sorce <idra@samba.org>
-   Copyright (C) 2007 Jelmer Vernooij <jelmer@samba.org>
+   Copyright (C) 2007-2008 Jelmer Vernooij <jelmer@samba.org>
 
      ** NOTE! The following LGPL license applies to the ldb
      ** library. This does NOT imply that all of Samba is released
@@ -34,6 +34,7 @@
 #include "talloc.h"
 #include "ldb.h"
 #include "ldb_errors.h"
+#include "ldb_private.h"
 
 typedef struct ldb_message ldb_msg;
 typedef struct ldb_context ldb;
@@ -49,6 +50,15 @@ typedef int ldb_error;
 %include "exception.i"
 %import "stdint.i"
 
+/* Don't expose talloc contexts in Python code. Python does reference 
+   counting for us, so just create a new top-level talloc context.
+ */
+%typemap(in, numinputs=0, noblock=1) TALLOC_CTX * {
+    $1 = NULL;
+}
+
+
+
 %constant int SCOPE_DEFAULT = LDB_SCOPE_DEFAULT;
 %constant int SCOPE_BASE = LDB_SCOPE_BASE;
 %constant int SCOPE_ONELEVEL = LDB_SCOPE_ONELEVEL;
@@ -66,13 +76,13 @@ typedef int ldb_error;
 /* The ldb functions will crash if a NULL ldb context is passed so
    catch this before it happens. */
 
-%typemap(check) struct ldb_context* {
+%typemap(check,noblock=1) struct ldb_context* {
        if ($1 == NULL)
                SWIG_exception(SWIG_ValueError, 
                        "ldb context must be non-NULL");
 }
 
-%typemap(check) ldb_msg * {
+%typemap(check,noblock=1) ldb_msg * {
        if ($1 == NULL)
                SWIG_exception(SWIG_ValueError, 
                        "Message can not be None");
@@ -82,7 +92,7 @@ typedef int ldb_error;
  * Wrap struct ldb_val
  */
 
-%typemap(in) struct ldb_val *INPUT (struct ldb_val temp) {
+%typemap(in,noblock=1) struct ldb_val *INPUT (struct ldb_val temp) {
        $1 = &temp;
        if (!PyString_Check($input)) {
                PyErr_SetString(PyExc_TypeError, "string arg expected");
@@ -92,20 +102,56 @@ typedef int ldb_error;
        $1->data = PyString_AsString($input);
 }
 
-%typemap(out) struct ldb_val {
-       $result = PyString_FromStringAndSize((const char *)$1.data, $1.length);
+%inline %{
+PyObject *ldb_val_to_py_object(struct ldb_context *ldb_ctx, 
+                               struct ldb_message_element *el, 
+                               struct ldb_val *val)
+{
+        const struct ldb_schema_attribute *a;
+        struct ldb_val new_val;
+        TALLOC_CTX *mem_ctx = talloc_new(NULL);
+        PyObject *ret;
+        
+        new_val = *val;
+        
+        if (ldb_ctx != NULL) {        
+               a = ldb_schema_attribute_by_name(ldb_ctx, el->name);
+        
+               if (a != NULL) {
+                       if (a->syntax->ldif_write_fn(ldb_ctx, mem_ctx, val, &new_val) != 0) {
+                               talloc_free(mem_ctx);
+                               return NULL;
+                       }
+               }
+        } 
+        
+       ret = PyString_FromStringAndSize((const char *)new_val.data, new_val.length);
+       
+       talloc_free(mem_ctx);
+       
+       return ret;
+}
+
+%}
+
+%typemap(out,noblock=1) struct ldb_val * {
+       $result = PyString_FromStringAndSize((const char *)$1->data, $1->length)
+}
+
+%typemap(out,noblock=1) struct ldb_val {
+       $result = PyString_FromStringAndSize((const char *)$1.data, $1.length)
 }
 
 /*
  * Wrap struct ldb_result
  */
 
-%typemap(in, numinputs=0) struct ldb_result **OUT (struct ldb_result *temp_ldb_result) {
+%typemap(in,noblock=1,numinputs=0) struct ldb_result **OUT (struct ldb_result *temp_ldb_result) {
        $1 = &temp_ldb_result;
 }
 
 #ifdef SWIGPYTHON
-%typemap(argout) struct ldb_result ** (int i) {
+%typemap(argout,noblock=1) struct ldb_result ** (int i) {
        $result = PyList_New((*$1)->count);
     for (i = 0; i < (*$1)->count; i++) {
         PyList_SetItem($result, i, 
@@ -114,7 +160,7 @@ typedef int ldb_error;
     }
 }
 
-%typemap(in, numinputs=1) const char * const *attrs {
+%typemap(in,noblock=1,numinputs=1) const char * const *NULL_STR_LIST {
     if ($input == Py_None) {
         $1 = NULL;
     } else if (PySequence_Check($input)) {
@@ -128,9 +174,13 @@ typedef int ldb_error;
     }
 }
 
-%typemap(freearg) const char * const *attrs {
+%typemap(freearg,noblock=1) const char * const *NULL_STR_LIST {
     talloc_free($1);
 }
+
+%apply const char * const *NULL_STR_LIST { const char * const *attrs }
+%apply const char * const *NULL_STR_LIST { const char * const *control_strings }
+
 #endif
 
 %types(struct ldb_result *);
@@ -145,9 +195,9 @@ typedef int ldb_error;
 %rename(Dn) ldb_dn;
 typedef struct ldb_dn {
     %extend {
-        ldb_dn(ldb *ldb, const char *str)
+        ldb_dn(ldb *ldb_ctx, const char *str)
         {
-            ldb_dn *ret = ldb_dn_new(ldb, ldb, str);
+            ldb_dn *ret = ldb_dn_new(ldb_ctx, ldb_ctx, str);
             /* ldb_dn_new() doesn't accept NULL as memory context, so 
                we do it this way... */
             talloc_steal(NULL, ret);
@@ -187,26 +237,39 @@ fail:
 
         /* FIXME: implement __getslice__ */
 #endif
+    %pythoncode {
+        def __eq__(self, other):
+            if isinstance(other, self.__class__):
+                return self.__cmp__(other) == 0
+            if isinstance(other, str):
+                return str(self) == other
+            return False
+    }
     }
 } ldb_dn;
 
 #ifdef SWIGPYTHON
 %{
 int ldb_dn_from_pyobject(TALLOC_CTX *mem_ctx, PyObject *object, 
-                         struct ldb_context *ldb, ldb_dn **dn)
+                         struct ldb_context *ldb_ctx, ldb_dn **dn)
 {
-    if (ldb != NULL && PyString_Check(object)) {
-        *dn = ldb_dn_new(mem_ctx, ldb, PyString_AsString(object));
+    int ret;
+    struct ldb_dn *odn;
+    if (ldb_ctx != NULL && PyString_Check(object)) {
+        *dn = ldb_dn_new(mem_ctx, ldb_ctx, PyString_AsString(object));
         return 0;
     }
-    return SWIG_ConvertPtr(object, (void **)dn, SWIGTYPE_p_ldb_dn, 
+    ret = SWIG_ConvertPtr(object, (void **)&odn, SWIGTYPE_p_ldb_dn, 
                            SWIG_POINTER_EXCEPTION);
+    *dn = ldb_dn_copy(mem_ctx, odn);
+    return ret;
 }
 
-ldb_msg_element *ldb_msg_element_from_pyobject(PyObject *set_obj, int flags,
+ldb_msg_element *ldb_msg_element_from_pyobject(TALLOC_CTX *mem_ctx,
+                                               PyObject *set_obj, int flags,
                                                const char *attr_name)
 {
-    struct ldb_message_element *me = talloc(NULL, struct ldb_message_element);
+    struct ldb_message_element *me = talloc(mem_ctx, struct ldb_message_element);
     me->name = attr_name;
     me->flags = flags;
     if (PyString_Check(set_obj)) {
@@ -232,7 +295,8 @@ ldb_msg_element *ldb_msg_element_from_pyobject(PyObject *set_obj, int flags,
     return me;
 }
 
-PyObject *ldb_msg_element_to_set(ldb_msg_element *me)
+PyObject *ldb_msg_element_to_set(struct ldb_context *ldb_ctx, 
+                                 ldb_msg_element *me)
 {
     int i;
     PyObject *result;
@@ -242,8 +306,7 @@ PyObject *ldb_msg_element_to_set(ldb_msg_element *me)
 
     for (i = 0; i < me->num_values; i++) {
         PyList_SetItem(result, i,
-            PyString_FromStringAndSize((const char *)me->values[i].data, 
-                                       me->values[i].length));
+            ldb_val_to_py_object(ldb_ctx, me, &me->values[i]));
     }
 
     return result;
@@ -260,22 +323,54 @@ typedef struct ldb_message_element {
 #ifdef SWIGPYTHON
         PyObject *__iter__(void)
         {
-            return PyObject_GetIter(ldb_msg_element_to_set($self));
+            return PyObject_GetIter(ldb_msg_element_to_set(NULL, $self));
         }
 
         PyObject *__set__(void)
         {
-            return ldb_msg_element_to_set($self);
+            return ldb_msg_element_to_set(NULL, $self);
         }
 
         ldb_msg_element(PyObject *set_obj, int flags=0, const char *name = NULL)
         {
-            return ldb_msg_element_from_pyobject(set_obj, flags, name);
+            return ldb_msg_element_from_pyobject(NULL, set_obj, flags, name);
+        }
+
+        int __len__()
+        {
+            return $self->num_values;
         }
 #endif
+
+        PyObject *get(int i)
+        {
+            if (i < 0 || i >= $self->num_values)
+                return Py_None;
+
+            return ldb_val_to_py_object(NULL, $self, &$self->values[i]);
+        }
+
         ~ldb_msg_element() { talloc_free($self); }
         int compare(ldb_msg_element *);
     }
+    %pythoncode {
+        def __getitem__(self, i):
+            ret = self.get(i)
+            if ret is None:
+                raise KeyError("no such value")
+            return ret
+
+        def __eq__(self, other):
+            if (len(self) == 1 and self.get(0) == other):
+                return True
+            if isinstance(other, self.__class__):
+                return self.__cmp__(other) == 0
+            o = iter(other)
+            for i in range(len(self)):
+                if self.get(i) != o.next():
+                    return False
+            return True
+    }
 } ldb_msg_element;
 
 /* ldb_message */
@@ -316,7 +411,6 @@ typedef struct ldb_message {
             return ret;
         }
         ~ldb_msg() { talloc_free($self); }
-
         ldb_msg_element *find_element(const char *name);
         
 #ifdef SWIGPYTHON
@@ -336,7 +430,7 @@ typedef struct ldb_message {
 
         void __setitem__(const char *attr_name, PyObject *val)
         {
-            struct ldb_message_element *el = ldb_msg_element_from_pyobject(
+            struct ldb_message_element *el = ldb_msg_element_from_pyobject(NULL,
                                                 val, 0, attr_name);
             talloc_steal($self, el);
             ldb_msg_remove_attr($self, attr_name);
@@ -373,13 +467,12 @@ static void py_ldb_debug(void *context, enum ldb_debug_level level, const char *
     PyObject *fn = context;
 
     vasprintf(&text, fmt, ap);
-    PyObject_CallFunction(fn, "(i,s)", level, text);
+    PyObject_CallFunction(fn, (char *)"(i,s)", level, text);
     free(text);
 }
 %}
 
-%typemap(in,numinputs=1) (void (*debug)(void *context, enum ldb_debug_level level, const char *fmt, va_list ap),
-                            void *context) {
+%typemap(in,numinputs=1,noblock=1) (void (*debug)(void *context, enum ldb_debug_level level, const char *fmt, va_list ap), void *context) {
     $1 = py_ldb_debug;
     /* FIXME: Should be decreased somewhere as well. Perhaps register a 
        destructor and tie it to the ldb context ? */
@@ -394,7 +487,7 @@ static void py_ldb_debug(void *context, enum ldb_debug_level level, const char *
         if (ldif == NULL) {
             return Py_None;
         } else {
-            return Py_BuildValue("(iO)", ldif->changetype, 
+            return Py_BuildValue((char *)"(iO)", ldif->changetype, 
                    SWIG_NewPointerObj(ldif->msg, SWIGTYPE_p_ldb_message, 0));
         }
     }
@@ -413,7 +506,7 @@ PyObject *PyExc_LdbError;
 %}
 
 %init %{
-    PyExc_LdbError = PyErr_NewException("_ldb.LdbError", NULL, NULL);
+    PyExc_LdbError = PyErr_NewException((char *)"_ldb.LdbError", NULL, NULL);
     PyDict_SetItemString(d, "LdbError", PyExc_LdbError);
 %}
 
@@ -425,48 +518,91 @@ PyObject *PyExc_LdbError;
  * Wrap ldb functions 
  */
 
+
+%typemap(out,noblock=1) ldb_error {
+    if ($1 != LDB_SUCCESS) {
+        PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", $1, ldb_strerror($1)));
+        SWIG_fail;
+    }
+    $result = Py_None;
+};
+
+%typemap(out,noblock=1) struct ldb_control ** {
+    if ($1 == NULL) {
+        PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(s)", ldb_errstring(arg1)));
+        SWIG_fail;
+    }
+    $result = SWIG_NewPointerObj($1, $1_descriptor, 0);
+}
+
 %rename(Ldb) ldb_context;
-/* Top-level ldb operations */
-typedef struct ldb_context {
-    %typemap(out) ldb_error {
-        if ($1 != LDB_SUCCESS) {
-            PyErr_SetObject(PyExc_LdbError, Py_BuildValue("(i,s)", $1, ldb_strerror($1)));
-            SWIG_fail;
-        }
-        $result = Py_None;
-    };
-    %extend {
-        ldb(const char *url=NULL, unsigned int flags = 0, 
-            const char *options[] = NULL)
-        {
-            ldb *ldb = ldb_init(NULL);
-            
-            if (url != NULL) {
-                int ret;
 
-                ret = ldb_connect(ldb, url, flags, options);
-                if (ret != LDB_SUCCESS)
-                    SWIG_exception(SWIG_ValueError, ldb_errstring(ldb));
-            }
+%typemap(in,noblock=1) struct ldb_dn * {
+    if (ldb_dn_from_pyobject(NULL, $input, arg1, &$1) != 0) {
+        SWIG_fail;
+    }
+};
 
-            return ldb;
+%typemap(freearg,noblock=1) struct ldb_dn * {
+    talloc_free($1);
+};
 
-fail:
-            talloc_free(ldb);
-            return NULL;
-        }
+/* Top-level ldb operations */
+typedef struct ldb_context {
+    %extend {
+        ldb(void) { return ldb_init(NULL); }
 
         ldb_error connect(const char *url, unsigned int flags = 0, 
             const char *options[] = NULL);
 
         ~ldb() { talloc_free($self); }
-        ldb_error search(ldb_dn *base = NULL, 
+        ldb_error search_ex(TALLOC_CTX *mem_ctx,
+                   ldb_dn *base = NULL, 
                    enum ldb_scope scope = LDB_SCOPE_DEFAULT, 
                    const char *expression = NULL, 
-                   const char * const *attrs = NULL, 
-                   struct ldb_result **OUT);
+                   const char *const *attrs = NULL, 
+                   struct ldb_control **controls = NULL,
+                   struct ldb_result **OUT) {
+            int ret;
+            struct ldb_result *res;
+            struct ldb_request *req;
+            res = talloc_zero(mem_ctx, struct ldb_result);
+            if (!res) {
+                return LDB_ERR_OPERATIONS_ERROR;
+            }
+
+            ret = ldb_build_search_req(&req, $self, mem_ctx,
+                           base?base:ldb_get_default_basedn($self),
+                           scope,
+                           expression,
+                           attrs,
+                           controls,
+                           res,
+                           ldb_search_default_callback);
+
+            if (ret != LDB_SUCCESS) {
+                talloc_free(res);
+                return ret;
+            }
+
+            ldb_set_timeout($self, req, 0); /* use default timeout */
+                
+            ret = ldb_request($self, req);
+                
+            if (ret == LDB_SUCCESS) {
+                ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+            }
+
+            talloc_free(req);
+
+            *OUT = res;
+            return ret;
+        }
+
         ldb_error delete(ldb_dn *dn);
         ldb_error rename(ldb_dn *olddn, ldb_dn *newdn);
+        struct ldb_control **parse_control_strings(TALLOC_CTX *mem_ctx, 
+                                                   const char * const*control_strings);
         ldb_error add(ldb_msg *add_msg);
         ldb_error add(PyObject *py_msg) 
         {
@@ -475,10 +611,10 @@ fail:
             PyObject *key, *value;
             ldb_msg_element *msgel;
             ldb_msg *msg = NULL;
+
             if (PyDict_Check(py_msg)) {
                 msg = ldb_msg_new(NULL);
-                msg->num_elements = PyDict_Size(py_msg) - 1; /* dn isn't in there */
-                msg->elements = talloc_zero_array(msg, struct ldb_message_element, msg->num_elements+1);
+                msg->elements = talloc_zero_array(msg, struct ldb_message_element, PyDict_Size(py_msg));
                 msg_pos = dict_pos = 0;
                 while (PyDict_Next(py_msg, &dict_pos, &key, &value)) {
                     if (!strcmp(PyString_AsString(key), "dn")) {
@@ -486,23 +622,28 @@ fail:
                             return LDB_ERR_OTHER;
                         }
                     } else {
-                        msgel = ldb_msg_element_from_pyobject(value, 0, PyString_AsString(key));
+                        msgel = ldb_msg_element_from_pyobject(msg->elements, value, 0, PyString_AsString(key));
+                        if (msgel == NULL) {
+                            SWIG_exception(SWIG_TypeError, "unable to import element");
+                            return LDB_ERR_OTHER;
+                        }
                         memcpy(&msg->elements[msg_pos], msgel, sizeof(*msgel));
                         msg_pos++;
                     }
-                    dict_pos++;
                 }
 
                 if (msg->dn == NULL) {
                     SWIG_exception(SWIG_TypeError, "no dn set");
                     return LDB_ERR_OTHER;
                 }
+
+                msg->num_elements = msg_pos;
             } else {
                 if (SWIG_ConvertPtr(py_msg, (void **)&msg, SWIGTYPE_p_ldb_message, 0) != 0)
                     return LDB_ERR_OTHER;
             }
 
-            ret = ldb_add($self,msg);
+            ret = ldb_add($self, msg);
 
             talloc_free(msg);
             return ret;
@@ -515,6 +656,35 @@ fail:
         ldb_dn *get_root_basedn();
         ldb_dn *get_schema_basedn();
         ldb_dn *get_default_basedn();
+        PyObject *schema_format_value(const char *element_name, PyObject *val)
+        {
+               const struct ldb_schema_attribute *a;
+               struct ldb_val old_val;
+               struct ldb_val new_val;
+               TALLOC_CTX *mem_ctx = talloc_new(NULL);
+               PyObject *ret;
+               
+               old_val.data = PyString_AsString(val);
+               old_val.length = PyString_Size(val);
+                
+               a = ldb_schema_attribute_by_name($self, element_name);
+        
+               if (a == NULL) {
+                       return Py_None;
+               }
+               
+               if (a->syntax->ldif_write_fn($self, mem_ctx, &old_val, &new_val) != 0) {
+                       talloc_free(mem_ctx);
+                       return Py_None;
+                }
+        
+               ret = PyString_FromStringAndSize((const char *)new_val.data, new_val.length);
+               
+               talloc_free(mem_ctx);
+               
+               return ret;
+        }
+
         const char *errstring();
         void set_create_perms(unsigned int perms);
         void set_modules_dir(const char *path);
@@ -526,11 +696,14 @@ fail:
         ldb_error transaction_start();
         ldb_error transaction_commit();
         ldb_error transaction_cancel();
-
+        void schema_attribute_remove(const char *name);
+        ldb_error schema_attribute_add(const char *attribute, unsigned flags, const char *syntax);
+       ldb_error setup_wellknown_attributes(void);
+       
 #ifdef SWIGPYTHON
-        %typemap(in,numinputs=0) struct ldb_result **result_as_bool (struct ldb_result *tmp) { $1 = &tmp; }
-        %typemap(argout) struct ldb_result **result_as_bool { $result = ((*$1)->count > 0)?Py_True:Py_False; }
-        %typemap(freearg) struct ldb_result **result_as_bool { talloc_free(*$1); }
+        %typemap(in,numinputs=0,noblock=1) struct ldb_result **result_as_bool (struct ldb_result *tmp) { $1 = &tmp; }
+        %typemap(argout,noblock=1) struct ldb_result **result_as_bool { $result = ((*$1)->count > 0)?Py_True:Py_False; }
+        %typemap(freearg,noblock=1) struct ldb_result **result_as_bool { talloc_free(*$1); }
         ldb_error __contains__(ldb_dn *dn, struct ldb_result **result_as_bool)
         {
             return ldb_search($self, dn, LDB_SCOPE_BASE, NULL, NULL, 
@@ -549,8 +722,26 @@ fail:
 
 #endif
     }
+    %pythoncode {
+        def __init__(self, url=None, flags=0, options=None):
+            _ldb.Ldb_swiginit(self,_ldb.new_Ldb())
+            if url is not None:
+                self.connect(url, flags, options)
+
+        def search(self, base=None, scope=SCOPE_DEFAULT, expression=None, 
+                   attrs=None, controls=None):
+            parsed_controls = None
+            if controls is not None:
+                parsed_controls = self.parse_control_strings(controls)
+            return self.search_ex(base, scope, expression, attrs, 
+                                  parsed_controls)
+    }
+
 } ldb;
 
+%typemap(in,noblock=1) struct ldb_dn *;
+%typemap(freearg,noblock=1) struct ldb_dn *;
+
 %nodefault ldb_message;
 %nodefault ldb_context;
 %nodefault Dn;
@@ -572,3 +763,12 @@ static char *timestring(time_t t)
 
 %rename(string_to_time) ldb_string_to_time;
 time_t ldb_string_to_time(const char *s);
+
+%typemap(in,noblock=1) const struct ldb_module_ops * {
+    $1 = talloc_zero(talloc_autofree_context(), struct ldb_module_ops);
+
+    $1->name = (char *)PyObject_GetAttrString($input, (char *)"name");
+}
+
+%rename(register_module) ldb_register_module;
+ldb_error ldb_register_module(const struct ldb_module_ops *);