r15113: Add a ldb_strerror() function.
[bbaumbach/samba-autobuild/.git] / source4 / lib / ldb / common / ldb.c
index 600c7063f01a4b400638c1e4b595a7e3c7321b16..eef02bd760d5e611ec8313d1efb11f5173ab6ef6 100644 (file)
  */
 
 #include "includes.h"
-#include "ldb/include/ldb.h"
-#include "ldb/include/ldb_private.h"
+#include "ldb/include/includes.h"
+
+/* 
+   initialise a ldb context
+   The mem_ctx is optional
+*/
+struct ldb_context *ldb_init(void *mem_ctx)
+{
+       struct ldb_context *ldb = talloc_zero(mem_ctx, struct ldb_context);
+       int ret;
+
+       ret = ldb_setup_wellknown_attributes(ldb);
+       if (ret != 0) {
+               talloc_free(ldb);
+               return NULL;
+       }
+
+       ldb_set_utf8_default(ldb);
+
+       return ldb;
+}
+
+static struct ldb_backend {
+       const char *name;
+       ldb_connect_fn connect_fn;
+       struct ldb_backend *prev, *next;
+} *ldb_backends = NULL;
+/*
+ register a new ldb backend
+*/
+int ldb_register_backend(const char *url_prefix, ldb_connect_fn connectfn)
+{
+       struct ldb_backend *backend = talloc(talloc_autofree_context(), struct ldb_backend);
+
+       /* Maybe check for duplicity here later on? */
+
+       backend->name = talloc_strdup(backend, url_prefix);
+       backend->connect_fn = connectfn;
+       DLIST_ADD(ldb_backends, backend);
+
+       return LDB_SUCCESS;
+}
+
+static ldb_connect_fn ldb_find_backend(const char *url)
+{
+       struct ldb_backend *backend;
+
+       for (backend = ldb_backends; backend; backend = backend->next) {
+               if (strncmp(backend->name, url, strlen(backend->name)) == 0) {
+                       return backend->connect_fn;
+               }
+       }
+
+       return NULL;
+}
 
 /* 
  connect to a database. The URL can either be one of the following forms
    the options are passed uninterpreted to the backend, and are
    backend specific
 */
-struct ldb_context *ldb_connect(const char *url, unsigned int flags,
-                               const char *options[])
+int ldb_connect(struct ldb_context *ldb, const char *url, unsigned int flags, const char *options[])
 {
-       struct ldb_context *ldb_ctx = NULL;
+       int ret;
+       char *backend;
+       ldb_connect_fn fn;
 
-       if (strncmp(url, "tdb:", 4) == 0 ||
-           strchr(url, ':') == NULL) {
-               ldb_ctx = ltdb_connect(url, flags, options);
+       if (strchr(url, ':') != NULL) {
+               backend = talloc_strndup(ldb, url, strchr(url, ':')-url);
+       } else {
+               /* Default to tdb */
+               backend = talloc_strdup(ldb, "tdb");
        }
 
-#if HAVE_LDAP
-       if (strncmp(url, "ldap", 4) == 0) {
-               ldb_ctx = lldb_connect(url, flags, options);
+       fn = ldb_find_backend(backend);
+
+       if (fn == NULL) {
+               if (ldb_try_load_dso(ldb, backend) == 0) {
+                       fn = ldb_find_backend(backend);
+               }
        }
-#endif
 
+       talloc_free(backend);
 
-       if (!ldb_ctx) {
-               errno = EINVAL;
-               return NULL;
+       if (fn == NULL) {
+               ldb_debug(ldb, LDB_DEBUG_FATAL, "Unable to find backend for '%s'\n", url);
+               return LDB_ERR_OTHER;
        }
 
-       if (ldb_load_modules(ldb_ctx, options) != 0) {
-               talloc_free(ldb_ctx);
-               errno = EINVAL;
-               return NULL;
+       ret = fn(ldb, url, flags, options);
+
+       if (ret != LDB_SUCCESS) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to connect to '%s'\n", url);
+               return ret;
+       }
+
+       if (ldb_load_modules(ldb, options) != LDB_SUCCESS) {
+               ldb_debug(ldb, LDB_DEBUG_FATAL, "Unable to load modules for '%s'\n", url);
+               return LDB_ERR_OTHER;
+       }
+
+       return LDB_SUCCESS;
+}
+
+void ldb_set_errstring(struct ldb_context *ldb, char *err_string)
+{
+       if (ldb->err_string) {
+               talloc_free(ldb->err_string);
+       }
+       ldb->err_string = talloc_steal(ldb, err_string);
+}
+
+void ldb_reset_err_string(struct ldb_context *ldb)
+{
+       if (ldb->err_string) {
+               talloc_free(ldb->err_string);
+               ldb->err_string = NULL;
        }
+}
+
+#define FIRST_OP(ldb, op) do { \
+       module = ldb->modules; \
+       while (module && module->ops->op == NULL) module = module->next; \
+       if (module == NULL) return LDB_ERR_OPERATIONS_ERROR; \
+} while (0)
+
+/*
+  start a transaction
+*/
+int ldb_transaction_start(struct ldb_context *ldb)
+{
+       struct ldb_module *module;
+       int status;
+       FIRST_OP(ldb, start_transaction);
+       
+       ldb->transaction_active++;
 
-       return ldb_ctx;
+       ldb_reset_err_string(ldb);
+
+       status = module->ops->start_transaction(module);
+       if (status != LDB_SUCCESS) {
+               if (ldb->err_string == NULL) {
+                       /* no error string was setup by the backend */
+                       ldb_set_errstring(ldb, 
+                                         talloc_asprintf(ldb, "ldb transaction start error %d", status));
+               }
+       }
+       return status;
+}
+
+/*
+  commit a transaction
+*/
+int ldb_transaction_commit(struct ldb_context *ldb)
+{
+       struct ldb_module *module;
+       int status;
+       FIRST_OP(ldb, end_transaction);
+
+       if (ldb->transaction_active > 0) {
+               ldb->transaction_active--;
+       } else {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ldb_reset_err_string(ldb);
+
+       status = module->ops->end_transaction(module);
+       if (status != LDB_SUCCESS) {
+               if (ldb->err_string == NULL) {
+                       /* no error string was setup by the backend */
+                       ldb_set_errstring(ldb, 
+                                         talloc_asprintf(ldb, "ldb transaction commit error %d", status));
+               }
+       }
+       return status;
+}
+
+/*
+  cancel a transaction
+*/
+int ldb_transaction_cancel(struct ldb_context *ldb)
+{
+       struct ldb_module *module;
+       int status;
+       FIRST_OP(ldb, del_transaction);
+
+       if (ldb->transaction_active > 0) {
+               ldb->transaction_active--;
+       } else {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       status = module->ops->del_transaction(module);
+       if (status != LDB_SUCCESS) {
+               if (ldb->err_string == NULL) {
+                       /* no error string was setup by the backend */
+                       ldb_set_errstring(ldb, 
+                                         talloc_asprintf(ldb, "ldb transaction cancel error %d", status));
+               }
+       }
+       return status;
+}
+
+int ldb_async_wait(struct ldb_async_handle *handle, enum ldb_async_wait_type type)
+{
+       return handle->module->ops->async_wait(handle, type);
+}
+
+/*
+  check for an error return from an op 
+  if an op fails, but has not setup an error string, then setup one now
+*/
+static int ldb_op_finish(struct ldb_context *ldb, int status)
+{
+       if (status == LDB_SUCCESS) {
+               return ldb_transaction_commit(ldb);
+       }
+       if (ldb->err_string == NULL) {
+               /* no error string was setup by the backend */
+               ldb_set_errstring(ldb, 
+                                 talloc_asprintf(ldb, "ldb error %d", status));
+       }
+       ldb_transaction_cancel(ldb);
+       return status;
+}
+
+/*
+  start an ldb request
+  autostarts a transacion if none active and the operation is not a search
+  NOTE: the request must be a talloc context.
+  returns LDB_ERR_* on errors.
+*/
+int ldb_request(struct ldb_context *ldb, struct ldb_request *req)
+{
+       int status, started_transaction=0;
+
+       ldb_reset_err_string(ldb);
+
+       if (req->operation == LDB_REQ_SEARCH) {
+               req->op.search.res = NULL;
+       }
+
+       /* start a transaction if needed */
+       if ((!ldb->transaction_active) &&
+           (req->operation == LDB_REQ_ADD ||
+            req->operation == LDB_REQ_MODIFY ||
+            req->operation == LDB_REQ_DELETE ||
+            req->operation == LDB_REQ_RENAME)) {
+               status = ldb_transaction_start(ldb);
+               if (status != LDB_SUCCESS) {
+                       talloc_free(req);
+                       return status;
+               }
+               started_transaction = 1;
+       }
+
+       /* call the first module in the chain */
+       status = ldb->modules->ops->request(ldb->modules, req);
+
+       if (started_transaction) {
+               return ldb_op_finish(ldb, status);
+       }
+
+       return status;
 }
 
 /*
@@ -86,12 +314,43 @@ struct ldb_context *ldb_connect(const char *url, unsigned int flags,
 
 */
 int ldb_search(struct ldb_context *ldb, 
-              const char *base,
+              const struct ldb_dn *base,
               enum ldb_scope scope,
               const char *expression,
-              const char * const *attrs, struct ldb_message ***res)
+              const char * const *attrs, 
+              struct ldb_result **res)
 {
-       return ldb->modules->ops->search(ldb->modules, base, scope, expression, attrs, res);
+       struct ldb_request *req;
+       int ret;
+
+       (*res) = NULL;
+
+       req = talloc(ldb, struct ldb_request);
+       if (req == NULL) {
+               ldb_set_errstring(ldb, talloc_strdup(ldb, "Out of memory!"));
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->operation = LDB_REQ_SEARCH;
+       req->op.search.base = base;
+       req->op.search.scope = scope;
+
+       req->op.search.tree = ldb_parse_tree(req, expression);
+       if (req->op.search.tree == NULL) {
+               ldb_set_errstring(ldb, talloc_strdup(ldb, "Unable to parse search expression"));
+               talloc_free(req);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->op.search.attrs = attrs;
+       req->controls = NULL;
+
+       ret = ldb_request(ldb, req);
+
+       (*res) = talloc_steal(ldb, req->op.search.res);
+
+       talloc_free(req);
+       return ret;
 }
 
 /*
@@ -101,7 +360,26 @@ int ldb_search(struct ldb_context *ldb,
 int ldb_add(struct ldb_context *ldb, 
            const struct ldb_message *message)
 {
-       return ldb->modules->ops->add_record(ldb->modules, message);
+       struct ldb_request *req;
+       int ret;
+
+       ret = ldb_msg_sanity_check(message);
+       if (ret != LDB_SUCCESS) return ret;
+
+       req = talloc(ldb, struct ldb_request);
+       if (req == NULL) {
+               ldb_set_errstring(ldb, talloc_strdup(ldb, "Out of memory!"));
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->operation = LDB_REQ_ADD;
+       req->op.add.message = message;
+       req->controls = NULL;
+
+       ret = ldb_request(ldb, req);
+
+       talloc_free(req);
+       return ret;
 }
 
 /*
@@ -110,31 +388,225 @@ int ldb_add(struct ldb_context *ldb,
 int ldb_modify(struct ldb_context *ldb, 
               const struct ldb_message *message)
 {
-       return ldb->modules->ops->modify_record(ldb->modules, message);
+       struct ldb_request *req;
+       int ret;
+
+       ret = ldb_msg_sanity_check(message);
+       if (ret != LDB_SUCCESS) return ret;
+
+       req = talloc(ldb, struct ldb_request);
+       if (req == NULL) {
+               ldb_set_errstring(ldb, talloc_strdup(ldb, "Out of memory!"));
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->operation = LDB_REQ_MODIFY;
+       req->op.add.message = message;
+       req->controls = NULL;
+
+       ret = ldb_request(ldb, req);
+
+       talloc_free(req);
+       return ret;
 }
 
 
 /*
   delete a record from the database
 */
-int ldb_delete(struct ldb_context *ldb, const char *dn)
+int ldb_delete(struct ldb_context *ldb, const struct ldb_dn *dn)
 {
-       return ldb->modules->ops->delete_record(ldb->modules, dn);
+       struct ldb_request *req;
+       int ret;
+
+       req = talloc(ldb, struct ldb_request);
+       if (req == NULL) {
+               ldb_set_errstring(ldb, talloc_strdup(ldb, "Out of memory!"));
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->operation = LDB_REQ_DELETE;
+       req->op.del.dn = dn;
+       req->controls = NULL;
+
+       ret = ldb_request(ldb, req);
+
+       talloc_free(req);
+       return ret;
 }
 
 /*
   rename a record in the database
 */
-int ldb_rename(struct ldb_context *ldb, const char *olddn, const char *newdn)
+int ldb_rename(struct ldb_context *ldb, const struct ldb_dn *olddn, const struct ldb_dn *newdn)
 {
-       return ldb->modules->ops->rename_record(ldb->modules, olddn, newdn);
+       struct ldb_request *req;
+       int ret;
+
+       req = talloc(ldb, struct ldb_request);
+       if (req == NULL) {
+               ldb_set_errstring(ldb, talloc_strdup(ldb, "Out of memory!"));
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->operation = LDB_REQ_RENAME;
+       req->op.rename.olddn = olddn;
+       req->op.rename.newdn = newdn;
+       req->controls = NULL;
+
+       ret = ldb_request(ldb, req);
+
+       talloc_free(req);
+       return ret;
 }
 
+
+
 /*
   return extended error information 
 */
 const char *ldb_errstring(struct ldb_context *ldb)
 {
-       return ldb->modules->ops->errstring(ldb->modules);
+       if (ldb->err_string) {
+               return ldb->err_string;
+       }
+
+       return NULL;
+}
+
+/*
+  return a string explaining what a ldb error constant meancs
+*/
+const char *ldb_strerror(int ldb_err)
+{
+       switch (ldb_err) {
+       case LDB_SUCCESS:
+               return "Success";
+       case LDB_ERR_OPERATIONS_ERROR:
+               return "Operations error";
+       case LDB_ERR_PROTOCOL_ERROR:
+               return "Protocol error";
+       case LDB_ERR_TIME_LIMIT_EXCEEDED:
+               return "Time limit exceeded";
+       case LDB_ERR_SIZE_LIMIT_EXCEEDED:
+               return "Size limit exceeded";
+       case LDB_ERR_COMPARE_FALSE:
+               return "Compare false";
+       case LDB_ERR_COMPARE_TRUE:
+               return "Compare true";
+       case LDB_ERR_AUTH_METHOD_NOT_SUPPORTED:
+               return "Auth method not supported";
+       case LDB_ERR_STRONG_AUTH_REQUIRED:
+               return "Strong auth required";
+/* 9 RESERVED */
+       case LDB_ERR_REFERRAL:
+               return "Referral error";
+       case LDB_ERR_ADMIN_LIMIT_EXCEEDED:
+               return "Admin limit exceeded";
+       case LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION:
+               return "Unsupported critical extension";
+       case LDB_ERR_CONFIDENTIALITY_REQUIRED:
+               return "Confidentiality required";
+       case LDB_ERR_SASL_BIND_IN_PROGRESS:
+               return "SASL bind in progress";
+       case LDB_ERR_NO_SUCH_ATTRIBUTE:
+               return "No such attribute";
+       case LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE:
+               return "Undefined attribute type";
+       case LDB_ERR_INAPPROPRIATE_MATCHING:
+               return "Inappropriate matching";
+       case LDB_ERR_CONSTRAINT_VIOLATION:
+               return "Constraint violation";
+       case LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS:
+               return "Attribute or value exists";
+       case LDB_ERR_INVALID_ATTRIBUTE_SYNTAX:
+               return "Invalid attribute syntax";
+/* 22-31 unused */
+       case LDB_ERR_NO_SUCH_OBJECT:
+               return "No such object";
+       case LDB_ERR_ALIAS_PROBLEM:
+               return "Alias problem";
+       case LDB_ERR_INVALID_DN_SYNTAX:
+               return "Invalid DN syntax";
+/* 53 RESERVED */
+       case LDB_ERR_ALIAS_DEREFERENCING_PROBLEM:
+               return "Alias dereferencing problem";
+/* 37-47 unused */
+       case LDB_ERR_INAPPROPRIATE_AUTHENTICATION:
+               return "Inappropriate authentication";
+       case LDB_ERR_INVALID_CREDENTIALS:
+               return "Invalid credentials";
+       case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+               return "insufficient access rights";
+       case LDB_ERR_BUSY:
+               return "Busy";
+       case LDB_ERR_UNAVAILABLE:
+               return "Unavailable";
+       case LDB_ERR_UNWILLING_TO_PERFORM:
+               return "Unwilling to perform";
+       case LDB_ERR_LOOP_DETECT:
+               return "Loop detect";
+/* 55-63 unused */
+       case LDB_ERR_NAMING_VIOLATION:
+               return "Naming violation";
+       case LDB_ERR_OBJECT_CLASS_VIOLATION:
+               return "Object class violation";
+       case LDB_ERR_NOT_ALLOWED_ON_NON_LEAF:
+               return "Not allowed on non-leaf";
+       case LDB_ERR_NOT_ALLOWED_ON_RDN:
+               return "Not allowed on RDN";
+       case LDB_ERR_ENTRY_ALREADY_EXISTS:
+               return "Entry already exists";
+       case LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED:
+               return "Object class mods prohibited";
+/* 70 RESERVED FOR CLDAP */
+       case LDB_ERR_AFFECTS_MULTIPLE_DSAS:
+               return "Affects multiple DSAs";
+/* 72-79 unused */
+       case LDB_ERR_OTHER:
+               return "Other";
+       }
+
+       return "Unknown error";
+}
+
+/*
+  set backend specific opaque parameters
+*/
+int ldb_set_opaque(struct ldb_context *ldb, const char *name, void *value)
+{
+       struct ldb_opaque *o;
+
+       /* allow updating an existing value */
+       for (o=ldb->opaque;o;o=o->next) {
+               if (strcmp(o->name, name) == 0) {
+                       o->value = value;
+                       return LDB_SUCCESS;
+               }
+       }
+
+       o = talloc(ldb, struct ldb_opaque);
+       if (o == NULL) {
+               ldb_oom(ldb);
+               return LDB_ERR_OTHER;
+       }
+       o->next = ldb->opaque;
+       o->name = name;
+       o->value = value;
+       ldb->opaque = o;
+       return LDB_SUCCESS;
 }
 
+/*
+  get a previously set opaque value
+*/
+void *ldb_get_opaque(struct ldb_context *ldb, const char *name)
+{
+       struct ldb_opaque *o;
+       for (o=ldb->opaque;o;o=o->next) {
+               if (strcmp(o->name, name) == 0) {
+                       return o->value;
+               }
+       }
+       return NULL;
+}