ldb:python bindings - add a context on "py_ldb_delete"
[ira/wip.git] / source4 / lib / ldb / pyldb.c
index 67e1d5c9e090e84da7c129e726b0f6ff50320321..1f1dcf8e316bca3ddd1ba2f1fb8fd57850ad1fab 100644 (file)
@@ -26,9 +26,9 @@
    License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */
 
+#include <Python.h>
 #include "replace.h"
 #include "ldb_private.h"
-#include <Python.h>
 #include "pyldb.h"
 
 /* There's no Py_ssize_t in 2.4, apparently */
@@ -656,6 +656,7 @@ static PyObject *py_ldb_modify(PyLdbObject *self, PyObject *args)
        Py_RETURN_NONE;
 }
 
+
 static PyObject *py_ldb_add(PyLdbObject *self, PyObject *args)
 {
        PyObject *py_msg;
@@ -663,21 +664,32 @@ static PyObject *py_ldb_add(PyLdbObject *self, PyObject *args)
        Py_ssize_t dict_pos, msg_pos;
        struct ldb_message_element *msgel;
        struct ldb_message *msg;
+       struct ldb_context *ldb_ctx;
+       struct ldb_request *req;
        PyObject *key, *value;
+       PyObject *py_controls = Py_None;
        TALLOC_CTX *mem_ctx;
+       struct ldb_control **parsed_controls;
 
-       if (!PyArg_ParseTuple(args, "O", &py_msg))
+       if (!PyArg_ParseTuple(args, "O|O", &py_msg, &py_controls ))
                return NULL;
+       ldb_ctx = PyLdb_AsLdbContext(self);
 
        mem_ctx = talloc_new(NULL);
-
+       if (py_controls == Py_None) {
+               parsed_controls = NULL;
+       } else {
+               const char **controls = PyList_AsStringList(ldb_ctx, py_controls, "controls");
+               parsed_controls = ldb_parse_control_strings(ldb_ctx, ldb_ctx, controls);
+               talloc_free(controls);
+       }
        if (PyDict_Check(py_msg)) {
                PyObject *dn_value = PyDict_GetItemString(py_msg, "dn");
                msg = ldb_msg_new(mem_ctx);
                msg->elements = talloc_zero_array(msg, struct ldb_message_element, PyDict_Size(py_msg));
                msg_pos = dict_pos = 0;
                if (dn_value) {
-                       if (!PyObject_AsDn(msg, dn_value, PyLdb_AsLdbContext(self), &msg->dn)) {
+                       if (!PyObject_AsDn(msg, dn_value, ldb_ctx, &msg->dn)) {
                                PyErr_SetString(PyExc_TypeError, "unable to import dn object");
                                talloc_free(mem_ctx);
                                return NULL;
@@ -713,8 +725,52 @@ static PyObject *py_ldb_add(PyLdbObject *self, PyObject *args)
        } else {
                msg = PyLdbMessage_AsMessage(py_msg);
        }
+        
+       ret = ldb_msg_sanity_check(ldb_ctx, msg);
+        if (ret != LDB_SUCCESS) {
+               PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, PyLdb_AsLdbContext(self));
+               talloc_free(mem_ctx);
+               return NULL;
+        }
+
+        ret = ldb_build_add_req(&req, ldb_ctx, ldb_ctx,
+                                        msg,
+                                        parsed_controls,
+                                        NULL,
+                                        ldb_op_default_callback,
+                                        NULL);
+
+        if (ret != LDB_SUCCESS) {
+               PyErr_SetString(PyExc_TypeError, "failed to build request");
+               talloc_free(mem_ctx);
+               return NULL;
+       }
+
+        /* do request and autostart a transaction */
+       /* Then let's LDB handle the message error in case of pb as they are meaningful */
+
+        ret = ldb_transaction_start(ldb_ctx);
+        if (ret != LDB_SUCCESS) {
+               talloc_free(req);
+               talloc_free(mem_ctx);
+               PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, PyLdb_AsLdbContext(self));
+        }
+
+        ret = ldb_request(ldb_ctx, req);
+        if (ret == LDB_SUCCESS) {
+                ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+        } 
 
-       ret = ldb_add(PyLdb_AsLdbContext(self), msg);
+       if (ret == LDB_SUCCESS) {
+                ret = ldb_transaction_commit(ldb_ctx);
+        } else {
+               ldb_transaction_cancel(ldb_ctx);
+               if (ldb_ctx->err_string == NULL) {
+                       /* no error string was setup by the backend */
+                       ldb_asprintf_errstring(ldb_ctx, "%s (%d)", ldb_strerror(ret), ret);
+               }
+       }
+       talloc_free(req);
        talloc_free(mem_ctx);
        PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, PyLdb_AsLdbContext(self));
 
@@ -727,15 +783,23 @@ static PyObject *py_ldb_delete(PyLdbObject *self, PyObject *args)
        struct ldb_dn *dn;
        int ret;
        struct ldb_context *ldb;
+       TALLOC_CTX *mem_ctx;
        if (!PyArg_ParseTuple(args, "O", &py_dn))
                return NULL;
 
+       mem_ctx = talloc_new(NULL);
+       if (mem_ctx == NULL) {
+               PyErr_NoMemory();
+               return NULL;
+       }
        ldb = PyLdb_AsLdbContext(self);
-
-       if (!PyObject_AsDn(NULL, py_dn, ldb, &dn))
+       if (!PyObject_AsDn(mem_ctx, py_dn, ldb, &dn)) {
+               talloc_free(mem_ctx);
                return NULL;
+       }
 
        ret = ldb_delete(ldb, dn);
+       talloc_free(mem_ctx);
        PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, ldb);
 
        Py_RETURN_NONE;
@@ -813,6 +877,41 @@ static PyObject *ldb_ldif_to_pyobject(struct ldb_ldif *ldif)
 }
 
 
+static PyObject *py_ldb_write_ldif(PyLdbMessageObject *self, PyObject *args)
+{
+       int changetype;
+       PyObject *py_msg;
+       struct ldb_ldif ldif;
+       PyObject *ret;
+       char *string;
+       TALLOC_CTX *mem_ctx;
+
+       if (!PyArg_ParseTuple(args, "Oi", &py_msg, &changetype))
+               return NULL;
+
+       if (!PyLdbMessage_Check(py_msg)) {
+               PyErr_SetString(PyExc_TypeError, "Expected Ldb Message for msg");
+               return NULL;
+       }
+
+       ldif.msg = PyLdbMessage_AsMessage(py_msg);
+       ldif.changetype = changetype;
+
+       mem_ctx = talloc_new(NULL);
+
+       string = ldb_ldif_write_string(PyLdb_AsLdbContext(self), mem_ctx, &ldif);
+       if (!string) {
+               PyErr_SetString(PyExc_KeyError, "Failed to generate LDIF");
+               return NULL;
+       }
+
+       ret = PyString_FromString(string);
+
+       talloc_free(mem_ctx);
+
+       return ret;
+}
+
 static PyObject *py_ldb_parse_ldif(PyLdbObject *self, PyObject *args)
 {
        PyObject *list;
@@ -866,8 +965,10 @@ static PyObject *py_ldb_msg_diff(PyLdbObject *self, PyObject *args)
        }
 
        diff = ldb_msg_diff(PyLdb_AsLdbContext(self), PyLdbMessage_AsMessage(py_msg_old), PyLdbMessage_AsMessage(py_msg_new));
-       if (diff == NULL) 
+       if (!diff) {
+               PyErr_SetString(PyExc_RuntimeError, "Failed to generate the Ldb Message diff");
                return NULL;
+       }
 
        py_ret = PyLdbMessage_FromMessage(diff);
 
@@ -913,7 +1014,7 @@ static PyObject *py_ldb_schema_format_value(PyLdbObject *self, PyObject *args)
 static PyObject *py_ldb_search(PyLdbObject *self, PyObject *args, PyObject *kwargs)
 {
        PyObject *py_base = Py_None;
-       enum ldb_scope scope = LDB_SCOPE_DEFAULT;
+       int scope = LDB_SCOPE_DEFAULT;
        char *expr = NULL;
        PyObject *py_attrs = Py_None;
        PyObject *py_controls = Py_None;
@@ -927,6 +1028,7 @@ static PyObject *py_ldb_search(PyLdbObject *self, PyObject *args, PyObject *kwar
        struct ldb_dn *base;
        PyObject *py_ret;
 
+       /* type "int" rather than "enum" for "scope" is intentional */
        if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OizOO",
                                         discard_const_p(char *, kwnames),
                                         &py_base, &scope, &expr, &py_attrs, &py_controls))
@@ -1116,6 +1218,9 @@ static PyMethodDef py_ldb_methods[] = {
        { "parse_ldif", (PyCFunction)py_ldb_parse_ldif, METH_VARARGS,
                "S.parse_ldif(ldif) -> iter(messages)\n"
                "Parse a string formatted using LDIF." },
+       { "write_ldif", (PyCFunction)py_ldb_write_ldif, METH_VARARGS,
+               "S.write_ldif(message, changetype) -> ldif\n"
+               "Print the message as a string formatted using LDIF." },
        { "msg_diff", (PyCFunction)py_ldb_msg_diff, METH_VARARGS,
                "S.msg_diff(Message) -> Message\n"
                "Return an LDB Message of the difference between two Message objects." },
@@ -1258,6 +1363,7 @@ static PyObject *py_ldb_module_search(PyLdbModuleObject *self, PyObject *args, P
        struct ldb_module *mod;
        const char * const*attrs;
 
+       /* type "int" rather than "enum" for "scope" is intentional */
        if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OiOO",
                                         discard_const_p(char *, kwnames),
                                         &py_base, &scope, &py_tree, &py_attrs))
@@ -1493,8 +1599,30 @@ static PyObject *py_ldb_msg_element_get(PyLdbMessageElementObject *self, PyObjec
                                                                 &(PyLdbMessageElement_AsMessageElement(self)->values[i]));
 }
 
+static PyObject *py_ldb_msg_element_flags(PyLdbMessageElementObject *self, PyObject *args)
+{
+       struct ldb_message_element *el;
+
+       el = PyLdbMessageElement_AsMessageElement(self);
+       return PyInt_FromLong(el->flags);
+}
+
+static PyObject *py_ldb_msg_element_set_flags(PyLdbMessageElementObject *self, PyObject *args)
+{
+       int flags;
+       struct ldb_message_element *el;
+       if (!PyArg_ParseTuple(args, "i", &flags))
+               return NULL;
+
+       el = PyLdbMessageElement_AsMessageElement(self);
+       el->flags = flags;
+       Py_RETURN_NONE;
+}
+
 static PyMethodDef py_ldb_msg_element_methods[] = {
        { "get", (PyCFunction)py_ldb_msg_element_get, METH_VARARGS, NULL },
+       { "set_flags", (PyCFunction)py_ldb_msg_element_set_flags, METH_VARARGS, NULL },
+       { "flags", (PyCFunction)py_ldb_msg_element_flags, METH_NOARGS, NULL },
        { NULL },
 };
 
@@ -1698,8 +1826,13 @@ static PyObject *py_ldb_msg_keys(PyLdbMessageObject *self)
 static PyObject *py_ldb_msg_getitem_helper(PyLdbMessageObject *self, PyObject *py_name)
 {
        struct ldb_message_element *el;
-       char *name = PyString_AsString(py_name);
+       char *name;
        struct ldb_message *msg = PyLdbMessage_AsMessage(self);
+       if (!PyString_Check(py_name)) {
+               PyErr_SetNone(PyExc_TypeError);
+               return NULL;
+       }
+       name = PyString_AsString(py_name);
        if (!strcmp(name, "dn"))
                return PyLdbDn_FromDn(msg->dn);
        el = ldb_msg_find_element(msg, name);
@@ -1726,8 +1859,11 @@ static PyObject *py_ldb_msg_get(PyLdbMessageObject *self, PyObject *args)
                return NULL;
 
        ret = py_ldb_msg_getitem_helper(self, name);
-       if (ret == NULL)
+       if (ret == NULL) {
+               if (PyErr_Occurred())
+                       return NULL;
                Py_RETURN_NONE;
+       }
        return ret;
 }
 
@@ -2244,10 +2380,12 @@ static PyObject *py_register_module(PyObject *module, PyObject *args)
 static PyObject *py_timestring(PyObject *module, PyObject *args)
 {
        time_t t;
+       unsigned long val;
        char *tresult;
        PyObject *ret;
-       if (!PyArg_ParseTuple(args, "L", &t))
+       if (!PyArg_ParseTuple(args, "l", &val))
                return NULL;
+       t = (time_t)val;
        tresult = ldb_timestring(NULL, t);
        ret = PyString_FromString(tresult);
        talloc_free(tresult);
@@ -2326,6 +2464,10 @@ void initldb(void)
        PyModule_AddObject(m, "CHANGETYPE_DELETE", PyInt_FromLong(LDB_CHANGETYPE_DELETE));
        PyModule_AddObject(m, "CHANGETYPE_MODIFY", PyInt_FromLong(LDB_CHANGETYPE_MODIFY));
 
+       PyModule_AddObject(m, "FLAG_MOD_ADD", PyInt_FromLong(LDB_FLAG_MOD_ADD));
+       PyModule_AddObject(m, "FLAG_MOD_REPLACE", PyInt_FromLong(LDB_FLAG_MOD_REPLACE));
+       PyModule_AddObject(m, "FLAG_MOD_DELETE", PyInt_FromLong(LDB_FLAG_MOD_DELETE));
+
        PyModule_AddObject(m, "SUCCESS", PyInt_FromLong(LDB_SUCCESS));
        PyModule_AddObject(m, "ERR_OPERATIONS_ERROR", PyInt_FromLong(LDB_ERR_OPERATIONS_ERROR));
        PyModule_AddObject(m, "ERR_PROTOCOL_ERROR", PyInt_FromLong(LDB_ERR_PROTOCOL_ERROR));