wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/*
|
||||
attribute handlers for well known attribute types, selected by syntax OID
|
||||
see rfc2252
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
#include "system/locale.h"
|
||||
#include "ldb/include/ldb_handlers.h"
|
||||
|
||||
/*
|
||||
default handler that just copies a ldb_val.
|
||||
*/
|
||||
int ldb_handler_copy(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
*out = ldb_val_dup(mem_ctx, in);
|
||||
if (in->length > 0 && out->data == NULL) {
|
||||
ldb_oom(ldb);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
a case folding copy handler, removing leading and trailing spaces and
|
||||
multiple internal spaces
|
||||
|
||||
We exploit the fact that utf8 never uses the space octet except for
|
||||
the space itself
|
||||
*/
|
||||
int ldb_handler_fold(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
char *s, *t;
|
||||
int l;
|
||||
if (!in || !out || !(in->data)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
out->data = (uint8_t *)ldb_casefold(ldb, mem_ctx, (const char *)(in->data));
|
||||
if (out->data == NULL) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_handler_fold: unable to casefold string [%s]", in->data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s = (char *)(out->data);
|
||||
|
||||
/* remove trailing spaces if any */
|
||||
l = strlen(s);
|
||||
while (l > 0 && s[l - 1] == ' ') l--;
|
||||
s[l] = '\0';
|
||||
|
||||
/* remove leading spaces if any */
|
||||
if (*s == ' ') {
|
||||
for (t = s; *s == ' '; s++) ;
|
||||
|
||||
/* remove leading spaces by moving down the string */
|
||||
memmove(t, s, l);
|
||||
|
||||
s = t;
|
||||
}
|
||||
|
||||
/* check middle spaces */
|
||||
while ((t = strchr(s, ' ')) != NULL) {
|
||||
for (s = t; *s == ' '; s++) ;
|
||||
|
||||
if ((s - t) > 1) {
|
||||
l = strlen(s);
|
||||
|
||||
/* remove all spaces but one by moving down the string */
|
||||
memmove(t + 1, s, l);
|
||||
}
|
||||
}
|
||||
|
||||
out->length = strlen((char *)out->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
canonicalise a ldap Integer
|
||||
rfc2252 specifies it should be in decimal form
|
||||
*/
|
||||
int ldb_canonicalise_Integer(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
char *end;
|
||||
long long i = strtoll((char *)in->data, &end, 0);
|
||||
if (*end != 0) {
|
||||
return -1;
|
||||
}
|
||||
out->data = (uint8_t *)talloc_asprintf(mem_ctx, "%lld", i);
|
||||
if (out->data == NULL) {
|
||||
return -1;
|
||||
}
|
||||
out->length = strlen((char *)out->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
compare two Integers
|
||||
*/
|
||||
int ldb_comparison_Integer(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
return strtoll((char *)v1->data, NULL, 0) - strtoll((char *)v2->data, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
compare two binary blobs
|
||||
*/
|
||||
int ldb_comparison_binary(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
if (v1->length != v2->length) {
|
||||
return v1->length - v2->length;
|
||||
}
|
||||
return memcmp(v1->data, v2->data, v1->length);
|
||||
}
|
||||
|
||||
/*
|
||||
compare two case insensitive strings, ignoring multiple whitespaces
|
||||
and leading and trailing whitespaces
|
||||
see rfc2252 section 8.1
|
||||
|
||||
try to optimize for the ascii case,
|
||||
but if we find out an utf8 codepoint revert to slower but correct function
|
||||
*/
|
||||
int ldb_comparison_fold(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
const char *s1=(const char *)v1->data, *s2=(const char *)v2->data;
|
||||
const char *u1, *u2;
|
||||
char *b1, *b2;
|
||||
int ret;
|
||||
while (*s1 == ' ') s1++;
|
||||
while (*s2 == ' ') s2++;
|
||||
/* TODO: make utf8 safe, possibly with helper function from application */
|
||||
while (*s1 && *s2) {
|
||||
/* the first 127 (0x7F) chars are ascii and utf8 guarantes they
|
||||
* never appear in multibyte sequences */
|
||||
if (((unsigned char)s1[0]) & 0x80) goto utf8str;
|
||||
if (((unsigned char)s2[0]) & 0x80) goto utf8str;
|
||||
if (toupper((unsigned char)*s1) != toupper((unsigned char)*s2))
|
||||
break;
|
||||
if (*s1 == ' ') {
|
||||
while (s1[0] == s1[1]) s1++;
|
||||
while (s2[0] == s2[1]) s2++;
|
||||
}
|
||||
s1++; s2++;
|
||||
}
|
||||
if (! (*s1 && *s2)) {
|
||||
/* check for trailing spaces only if one of the pointers
|
||||
* has reached the end of the strings otherwise we
|
||||
* can mistakenly match.
|
||||
* ex. "domain users" <-> "domainUpdates"
|
||||
*/
|
||||
while (*s1 == ' ') s1++;
|
||||
while (*s2 == ' ') s2++;
|
||||
}
|
||||
return (int)(toupper(*s1)) - (int)(toupper(*s2));
|
||||
|
||||
utf8str:
|
||||
/* no need to recheck from the start, just from the first utf8 char found */
|
||||
b1 = ldb_casefold(ldb, mem_ctx, s1);
|
||||
b2 = ldb_casefold(ldb, mem_ctx, s2);
|
||||
|
||||
if (b1 && b2) {
|
||||
/* Both strings converted correctly */
|
||||
|
||||
u1 = b1;
|
||||
u2 = b2;
|
||||
} else {
|
||||
/* One of the strings was not UTF8, so we have no options but to do a binary compare */
|
||||
|
||||
u1 = s1;
|
||||
u2 = s2;
|
||||
}
|
||||
|
||||
while (*u1 & *u2) {
|
||||
if (*u1 != *u2)
|
||||
break;
|
||||
if (*u1 == ' ') {
|
||||
while (u1[0] == u1[1]) u1++;
|
||||
while (u2[0] == u2[1]) u2++;
|
||||
}
|
||||
u1++; u2++;
|
||||
}
|
||||
if (! (*u1 && *u2)) {
|
||||
while (*u1 == ' ') u1++;
|
||||
while (*u2 == ' ') u2++;
|
||||
}
|
||||
ret = (int)(*u1 - *u2);
|
||||
|
||||
talloc_free(b1);
|
||||
talloc_free(b2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
canonicalise a attribute in DN format
|
||||
*/
|
||||
int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
struct ldb_dn *dn;
|
||||
int ret = -1;
|
||||
|
||||
out->length = 0;
|
||||
out->data = NULL;
|
||||
|
||||
dn = ldb_dn_new(ldb, mem_ctx, (char *)in->data);
|
||||
if ( ! ldb_dn_validate(dn)) {
|
||||
return LDB_ERR_INVALID_DN_SYNTAX;
|
||||
}
|
||||
|
||||
out->data = (uint8_t *)ldb_dn_alloc_casefold(mem_ctx, dn);
|
||||
if (out->data == NULL) {
|
||||
goto done;
|
||||
}
|
||||
out->length = strlen((char *)out->data);
|
||||
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
talloc_free(dn);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
compare two dns
|
||||
*/
|
||||
int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
struct ldb_dn *dn1 = NULL, *dn2 = NULL;
|
||||
int ret;
|
||||
|
||||
dn1 = ldb_dn_new(ldb, mem_ctx, (char *)v1->data);
|
||||
if ( ! ldb_dn_validate(dn1)) return -1;
|
||||
|
||||
dn2 = ldb_dn_new(ldb, mem_ctx, (char *)v2->data);
|
||||
if ( ! ldb_dn_validate(dn2)) {
|
||||
talloc_free(dn1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ldb_dn_compare(dn1, dn2);
|
||||
|
||||
talloc_free(dn1);
|
||||
talloc_free(dn2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
compare two objectclasses, looking at subclasses
|
||||
*/
|
||||
int ldb_comparison_objectclass(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
int ret, i;
|
||||
const char **subclasses;
|
||||
ret = ldb_comparison_fold(ldb, mem_ctx, v1, v2);
|
||||
if (ret == 0) {
|
||||
return 0;
|
||||
}
|
||||
subclasses = ldb_subclass_list(ldb, (char *)v1->data);
|
||||
if (subclasses == NULL) {
|
||||
return ret;
|
||||
}
|
||||
for (i=0;subclasses[i];i++) {
|
||||
struct ldb_val vs;
|
||||
vs.data = discard_const(subclasses[i]);
|
||||
vs.length = strlen(subclasses[i]);
|
||||
if (ldb_comparison_objectclass(ldb, mem_ctx, &vs, v2) == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
compare two utc time values. 1 second resolution
|
||||
*/
|
||||
int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
time_t t1, t2;
|
||||
t1 = ldb_string_to_time((char *)v1->data);
|
||||
t2 = ldb_string_to_time((char *)v2->data);
|
||||
return (int)t2 - (int)t1;
|
||||
}
|
||||
|
||||
/*
|
||||
canonicalise a utc time
|
||||
*/
|
||||
int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
time_t t = ldb_string_to_time((char *)in->data);
|
||||
out->data = (uint8_t *)ldb_timestring(mem_ctx, t);
|
||||
if (out->data == NULL) {
|
||||
return -1;
|
||||
}
|
||||
out->length = strlen((char *)out->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
table of standard attribute handlers
|
||||
*/
|
||||
static const struct ldb_attrib_handler ldb_standard_attribs[] = {
|
||||
{
|
||||
.attr = LDB_SYNTAX_INTEGER,
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_Integer,
|
||||
.comparison_fn = ldb_comparison_Integer
|
||||
},
|
||||
{
|
||||
.attr = LDB_SYNTAX_OCTET_STRING,
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_handler_copy,
|
||||
.comparison_fn = ldb_comparison_binary
|
||||
},
|
||||
{
|
||||
.attr = LDB_SYNTAX_DIRECTORY_STRING,
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_handler_fold,
|
||||
.comparison_fn = ldb_comparison_fold
|
||||
},
|
||||
{
|
||||
.attr = LDB_SYNTAX_DN,
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_dn,
|
||||
.comparison_fn = ldb_comparison_dn
|
||||
},
|
||||
{
|
||||
.attr = LDB_SYNTAX_OBJECTCLASS,
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_handler_fold,
|
||||
.comparison_fn = ldb_comparison_objectclass
|
||||
},
|
||||
{
|
||||
.attr = LDB_SYNTAX_UTC_TIME,
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_utctime,
|
||||
.comparison_fn = ldb_comparison_utctime
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
return the attribute handlers for a given syntax name
|
||||
*/
|
||||
const struct ldb_attrib_handler *ldb_attrib_handler_syntax(struct ldb_context *ldb,
|
||||
const char *syntax)
|
||||
{
|
||||
int i;
|
||||
unsigned num_handlers = sizeof(ldb_standard_attribs)/sizeof(ldb_standard_attribs[0]);
|
||||
/* TODO: should be replaced with a binary search */
|
||||
for (i=0;i<num_handlers;i++) {
|
||||
if (strcmp(ldb_standard_attribs[i].attr, syntax) == 0) {
|
||||
return &ldb_standard_attribs[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/*
|
||||
register handlers for specific attributes and objectclass relationships
|
||||
|
||||
this allows a backend to store its schema information in any format
|
||||
it likes (or to not have any schema information at all) while keeping the
|
||||
message matching logic generic
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
/*
|
||||
add to the list of ldif handlers for this ldb context
|
||||
*/
|
||||
int ldb_set_attrib_handlers(struct ldb_context *ldb,
|
||||
const struct ldb_attrib_handler *handlers,
|
||||
unsigned num_handlers)
|
||||
{
|
||||
int i, j, n;
|
||||
struct ldb_attrib_handler *h;
|
||||
n = ldb->schema.num_attrib_handlers + num_handlers;
|
||||
h = talloc_realloc(ldb, ldb->schema.attrib_handlers,
|
||||
struct ldb_attrib_handler, n);
|
||||
if (h == NULL) {
|
||||
ldb_oom(ldb);
|
||||
return -1;
|
||||
}
|
||||
ldb->schema.attrib_handlers = h;
|
||||
|
||||
for (i = 0; i < num_handlers; i++) {
|
||||
for (j = 0; j < ldb->schema.num_attrib_handlers; j++) {
|
||||
if (ldb_attr_cmp(handlers[i].attr, h[j].attr) < 0) {
|
||||
memmove(h+j+1, h+j, sizeof(*h) * (ldb->schema.num_attrib_handlers-j));
|
||||
break;
|
||||
}
|
||||
}
|
||||
h[j] = handlers[i];
|
||||
if (h[j].flags & LDB_ATTR_FLAG_ALLOCATED) {
|
||||
h[j].attr = talloc_strdup(h, h[j].attr);
|
||||
if (h[j].attr == NULL) {
|
||||
ldb_oom(ldb);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ldb->schema.num_attrib_handlers++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
default handler function pointers
|
||||
*/
|
||||
static const struct ldb_attrib_handler ldb_default_attrib_handler = {
|
||||
.attr = NULL,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_handler_copy,
|
||||
.comparison_fn = ldb_comparison_binary,
|
||||
};
|
||||
|
||||
/*
|
||||
return the attribute handlers for a given attribute
|
||||
*/
|
||||
const struct ldb_attrib_handler *ldb_attrib_handler(struct ldb_context *ldb,
|
||||
const char *attrib)
|
||||
{
|
||||
int i, e, b = 0, r;
|
||||
const struct ldb_attrib_handler *def = &ldb_default_attrib_handler;
|
||||
|
||||
/* as handlers are sorted, '*' must be the first if present */
|
||||
if (strcmp(ldb->schema.attrib_handlers[0].attr, "*") == 0) {
|
||||
def = &ldb->schema.attrib_handlers[0];
|
||||
b = 1;
|
||||
}
|
||||
|
||||
/* do a binary search on the array */
|
||||
e = ldb->schema.num_attrib_handlers - 1;
|
||||
|
||||
while (b <= e) {
|
||||
|
||||
i = (b + e) / 2;
|
||||
|
||||
r = ldb_attr_cmp(attrib, ldb->schema.attrib_handlers[i].attr);
|
||||
if (r == 0) {
|
||||
return &ldb->schema.attrib_handlers[i];
|
||||
}
|
||||
if (r < 0) {
|
||||
e = i - 1;
|
||||
} else {
|
||||
b = i + 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
add to the list of ldif handlers for this ldb context
|
||||
*/
|
||||
void ldb_remove_attrib_handler(struct ldb_context *ldb, const char *attrib)
|
||||
{
|
||||
const struct ldb_attrib_handler *h;
|
||||
int i;
|
||||
h = ldb_attrib_handler(ldb, attrib);
|
||||
if (h == &ldb_default_attrib_handler) {
|
||||
return;
|
||||
}
|
||||
if (h->flags & LDB_ATTR_FLAG_ALLOCATED) {
|
||||
talloc_free(discard_const_p(char, h->attr));
|
||||
}
|
||||
i = h - ldb->schema.attrib_handlers;
|
||||
if (i < ldb->schema.num_attrib_handlers - 1) {
|
||||
memmove(&ldb->schema.attrib_handlers[i],
|
||||
h+1, sizeof(*h) * (ldb->schema.num_attrib_handlers-(i+1)));
|
||||
}
|
||||
ldb->schema.num_attrib_handlers--;
|
||||
}
|
||||
|
||||
/*
|
||||
setup a attribute handler using a standard syntax
|
||||
*/
|
||||
int ldb_set_attrib_handler_syntax(struct ldb_context *ldb,
|
||||
const char *attr, const char *syntax)
|
||||
{
|
||||
const struct ldb_attrib_handler *h = ldb_attrib_handler_syntax(ldb, syntax);
|
||||
struct ldb_attrib_handler h2;
|
||||
if (h == NULL) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "Unknown syntax '%s'\n", syntax);
|
||||
return -1;
|
||||
}
|
||||
h2 = *h;
|
||||
h2.attr = attr;
|
||||
return ldb_set_attrib_handlers(ldb, &h2, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
setup the attribute handles for well known attributes
|
||||
*/
|
||||
int ldb_setup_wellknown_attributes(struct ldb_context *ldb)
|
||||
{
|
||||
const struct {
|
||||
const char *attr;
|
||||
const char *syntax;
|
||||
} wellknown[] = {
|
||||
{ "dn", LDB_SYNTAX_DN },
|
||||
{ "distinguishedName", LDB_SYNTAX_DN },
|
||||
{ "cn", LDB_SYNTAX_DIRECTORY_STRING },
|
||||
{ "dc", LDB_SYNTAX_DIRECTORY_STRING },
|
||||
{ "ou", LDB_SYNTAX_DIRECTORY_STRING },
|
||||
{ "objectClass", LDB_SYNTAX_OBJECTCLASS }
|
||||
};
|
||||
int i;
|
||||
for (i=0;i<ARRAY_SIZE(wellknown);i++) {
|
||||
if (ldb_set_attrib_handler_syntax(ldb, wellknown[i].attr,
|
||||
wellknown[i].syntax) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
return the list of subclasses for a class
|
||||
*/
|
||||
const char **ldb_subclass_list(struct ldb_context *ldb, const char *classname)
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<ldb->schema.num_classes;i++) {
|
||||
if (ldb_attr_cmp(classname, ldb->schema.classes[i].name) == 0) {
|
||||
return (const char **)ldb->schema.classes[i].subclasses;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
add a new subclass
|
||||
*/
|
||||
static int ldb_subclass_new(struct ldb_context *ldb, const char *classname, const char *subclass)
|
||||
{
|
||||
struct ldb_subclass *s, *c;
|
||||
s = talloc_realloc(ldb, ldb->schema.classes, struct ldb_subclass, ldb->schema.num_classes+1);
|
||||
if (s == NULL) goto failed;
|
||||
|
||||
ldb->schema.classes = s;
|
||||
c = &s[ldb->schema.num_classes];
|
||||
c->name = talloc_strdup(s, classname);
|
||||
if (c->name == NULL) goto failed;
|
||||
|
||||
c->subclasses = talloc_array(s, char *, 2);
|
||||
if (c->subclasses == NULL) goto failed;
|
||||
|
||||
c->subclasses[0] = talloc_strdup(c->subclasses, subclass);
|
||||
if (c->subclasses[0] == NULL) goto failed;
|
||||
c->subclasses[1] = NULL;
|
||||
|
||||
ldb->schema.num_classes++;
|
||||
|
||||
return 0;
|
||||
failed:
|
||||
ldb_oom(ldb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
add a subclass
|
||||
*/
|
||||
int ldb_subclass_add(struct ldb_context *ldb, const char *classname, const char *subclass)
|
||||
{
|
||||
int i, n;
|
||||
struct ldb_subclass *c;
|
||||
char **s;
|
||||
|
||||
for (i=0;i<ldb->schema.num_classes;i++) {
|
||||
if (ldb_attr_cmp(classname, ldb->schema.classes[i].name) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == ldb->schema.num_classes) {
|
||||
return ldb_subclass_new(ldb, classname, subclass);
|
||||
}
|
||||
c = &ldb->schema.classes[i];
|
||||
|
||||
for (n=0;c->subclasses[n];n++) /* noop */;
|
||||
|
||||
s = talloc_realloc(ldb->schema.classes, c->subclasses, char *, n+2);
|
||||
if (s == NULL) {
|
||||
ldb_oom(ldb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
c->subclasses = s;
|
||||
s[n] = talloc_strdup(s, subclass);
|
||||
if (s[n] == NULL) {
|
||||
ldb_oom(ldb);
|
||||
return -1;
|
||||
}
|
||||
s[n+1] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
remove a set of subclasses for a class
|
||||
*/
|
||||
void ldb_subclass_remove(struct ldb_context *ldb, const char *classname)
|
||||
{
|
||||
int i;
|
||||
struct ldb_subclass *c;
|
||||
|
||||
for (i=0;i<ldb->schema.num_classes;i++) {
|
||||
if (ldb_attr_cmp(classname, ldb->schema.classes[i].name) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == ldb->schema.num_classes) {
|
||||
return;
|
||||
}
|
||||
|
||||
c = &ldb->schema.classes[i];
|
||||
talloc_free(c->name);
|
||||
talloc_free(c->subclasses);
|
||||
if (ldb->schema.num_classes-(i+1) > 0) {
|
||||
memmove(c, c+1, sizeof(*c) * (ldb->schema.num_classes-(i+1)));
|
||||
}
|
||||
ldb->schema.num_classes--;
|
||||
if (ldb->schema.num_classes == 0) {
|
||||
talloc_free(ldb->schema.classes);
|
||||
ldb->schema.classes = NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Simo Sorce 2005
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb_controls.c
|
||||
*
|
||||
* Component: ldb controls utility functions
|
||||
*
|
||||
* Description: helper functions for control modules
|
||||
*
|
||||
* Author: Simo Sorce
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
/* check if a control with the specified "oid" exist and return it */
|
||||
/* returns NULL if not found */
|
||||
struct ldb_control *get_control_from_list(struct ldb_control **controls, const char *oid)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* check if there's a paged request control */
|
||||
if (controls != NULL) {
|
||||
for (i = 0; controls[i]; i++) {
|
||||
if (strcmp(oid, controls[i]->oid) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return controls[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* saves the current controls list into the "saver" and replace the one in req with a new one excluding
|
||||
the "exclude" control */
|
||||
/* returns False on error */
|
||||
int save_controls(struct ldb_control *exclude, struct ldb_request *req, struct ldb_control ***saver)
|
||||
{
|
||||
struct ldb_control **lcs;
|
||||
int i, j;
|
||||
|
||||
*saver = req->controls;
|
||||
for (i = 0; req->controls[i]; i++);
|
||||
if (i == 1) {
|
||||
req->controls = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
lcs = talloc_array(req, struct ldb_control *, i);
|
||||
if (!lcs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0, j = 0; (*saver)[i]; i++) {
|
||||
if (exclude == (*saver)[i]) continue;
|
||||
lcs[j] = (*saver)[i];
|
||||
j++;
|
||||
}
|
||||
lcs[j] = NULL;
|
||||
|
||||
req->controls = lcs;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* check if there's any control marked as critical in the list */
|
||||
/* return True if any, False if none */
|
||||
int check_critical_controls(struct ldb_control **controls)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (controls == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; controls[i]; i++) {
|
||||
if (controls[i]->critical) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb debug
|
||||
*
|
||||
* Description: functions for printing debug messages
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
/*
|
||||
this allows the user to choose their own debug function
|
||||
*/
|
||||
int ldb_set_debug(struct ldb_context *ldb,
|
||||
void (*debug)(void *context, enum ldb_debug_level level,
|
||||
const char *fmt, va_list ap),
|
||||
void *context)
|
||||
{
|
||||
ldb->debug_ops.debug = debug;
|
||||
ldb->debug_ops.context = context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
debug function for ldb_set_debug_stderr
|
||||
*/
|
||||
static void ldb_debug_stderr(void *context, enum ldb_debug_level level,
|
||||
const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0);
|
||||
static void ldb_debug_stderr(void *context, enum ldb_debug_level level,
|
||||
const char *fmt, va_list ap)
|
||||
{
|
||||
if (level <= LDB_DEBUG_WARNING) {
|
||||
vfprintf(stderr, fmt, ap);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
convenience function to setup debug messages on stderr
|
||||
messages of level LDB_DEBUG_WARNING and higher are printed
|
||||
*/
|
||||
int ldb_set_debug_stderr(struct ldb_context *ldb)
|
||||
{
|
||||
return ldb_set_debug(ldb, ldb_debug_stderr, ldb);
|
||||
}
|
||||
|
||||
/*
|
||||
log a message
|
||||
*/
|
||||
void ldb_debug(struct ldb_context *ldb, enum ldb_debug_level level, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
if (ldb->debug_ops.debug == NULL) {
|
||||
ldb_set_debug_stderr(ldb);
|
||||
}
|
||||
va_start(ap, fmt);
|
||||
ldb->debug_ops.debug(ldb->debug_ops.context, level, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
log a message, and set the ldb error string to the same message
|
||||
*/
|
||||
void ldb_debug_set(struct ldb_context *ldb, enum ldb_debug_level level,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *msg;
|
||||
va_start(ap, fmt);
|
||||
msg = talloc_vasprintf(ldb, fmt, ap);
|
||||
va_end(ap);
|
||||
if (msg != NULL) {
|
||||
ldb_set_errstring(ldb, msg);
|
||||
ldb_debug(ldb, level, "%s", msg);
|
||||
}
|
||||
talloc_free(msg);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,761 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldif routines
|
||||
*
|
||||
* Description: ldif pack/unpack routines
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*/
|
||||
|
||||
/*
|
||||
see RFC2849 for the LDIF format definition
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
#include "system/locale.h"
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
static int ldb_read_data_file(void *mem_ctx, struct ldb_val *value)
|
||||
{
|
||||
struct stat statbuf;
|
||||
char *buf;
|
||||
int count, size, bytes;
|
||||
int ret;
|
||||
int f;
|
||||
const char *fname = (const char *)value->data;
|
||||
|
||||
if (strncmp(fname, "file://", 7) != 0) {
|
||||
return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
|
||||
}
|
||||
fname += 7;
|
||||
|
||||
f = open(fname, O_RDONLY);
|
||||
if (f == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fstat(f, &statbuf) != 0) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (statbuf.st_size == 0) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
value->data = (uint8_t *)talloc_size(mem_ctx, statbuf.st_size + 1);
|
||||
if (value->data == NULL) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
value->data[statbuf.st_size] = 0;
|
||||
|
||||
count = 0;
|
||||
size = statbuf.st_size;
|
||||
buf = (char *)value->data;
|
||||
while (count < statbuf.st_size) {
|
||||
bytes = read(f, buf, size);
|
||||
if (bytes == -1) {
|
||||
talloc_free(value->data);
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
count += bytes;
|
||||
buf += bytes;
|
||||
size -= bytes;
|
||||
}
|
||||
|
||||
value->length = statbuf.st_size;
|
||||
ret = statbuf.st_size;
|
||||
|
||||
done:
|
||||
close(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
this base64 decoder was taken from jitterbug (written by tridge).
|
||||
we might need to replace it with a new version
|
||||
*/
|
||||
int ldb_base64_decode(char *s)
|
||||
{
|
||||
const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
int bit_offset=0, byte_offset, idx, i, n;
|
||||
uint8_t *d = (uint8_t *)s;
|
||||
char *p=NULL;
|
||||
|
||||
n=i=0;
|
||||
|
||||
while (*s && (p=strchr(b64,*s))) {
|
||||
idx = (int)(p - b64);
|
||||
byte_offset = (i*6)/8;
|
||||
bit_offset = (i*6)%8;
|
||||
d[byte_offset] &= ~((1<<(8-bit_offset))-1);
|
||||
if (bit_offset < 3) {
|
||||
d[byte_offset] |= (idx << (2-bit_offset));
|
||||
n = byte_offset+1;
|
||||
} else {
|
||||
d[byte_offset] |= (idx >> (bit_offset-2));
|
||||
d[byte_offset+1] = 0;
|
||||
d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF;
|
||||
n = byte_offset+2;
|
||||
}
|
||||
s++; i++;
|
||||
}
|
||||
if (bit_offset >= 3) {
|
||||
n--;
|
||||
}
|
||||
|
||||
if (*s && !p) {
|
||||
/* the only termination allowed */
|
||||
if (*s != '=') {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* null terminate */
|
||||
d[n] = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
encode as base64
|
||||
caller frees
|
||||
*/
|
||||
char *ldb_base64_encode(void *mem_ctx, const char *buf, int len)
|
||||
{
|
||||
const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
int bit_offset, byte_offset, idx, i;
|
||||
const uint8_t *d = (const uint8_t *)buf;
|
||||
int bytes = (len*8 + 5)/6, pad_bytes = (bytes % 4) ? 4 - (bytes % 4) : 0;
|
||||
char *out;
|
||||
|
||||
out = talloc_array(mem_ctx, char, bytes+pad_bytes+1);
|
||||
if (!out) return NULL;
|
||||
|
||||
for (i=0;i<bytes;i++) {
|
||||
byte_offset = (i*6)/8;
|
||||
bit_offset = (i*6)%8;
|
||||
if (bit_offset < 3) {
|
||||
idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
|
||||
} else {
|
||||
idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
|
||||
if (byte_offset+1 < len) {
|
||||
idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
|
||||
}
|
||||
}
|
||||
out[i] = b64[idx];
|
||||
}
|
||||
|
||||
for (;i<bytes+pad_bytes;i++)
|
||||
out[i] = '=';
|
||||
out[i] = 0;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
see if a buffer should be base64 encoded
|
||||
*/
|
||||
int ldb_should_b64_encode(const struct ldb_val *val)
|
||||
{
|
||||
unsigned int i;
|
||||
uint8_t *p = val->data;
|
||||
|
||||
if (val->length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (p[0] == ' ' || p[0] == ':') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i=0; i<val->length; i++) {
|
||||
if (!isprint(p[i]) || p[i] == '\n') {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this macro is used to handle the return checking on fprintf_fn() */
|
||||
#define CHECK_RET do { if (ret < 0) return ret; total += ret; } while (0)
|
||||
|
||||
/*
|
||||
write a line folded string onto a file
|
||||
*/
|
||||
static int fold_string(int (*fprintf_fn)(void *, const char *, ...), void *private_data,
|
||||
const char *buf, size_t length, int start_pos)
|
||||
{
|
||||
unsigned int i;
|
||||
int total=0, ret;
|
||||
|
||||
for (i=0;i<length;i++) {
|
||||
ret = fprintf_fn(private_data, "%c", buf[i]);
|
||||
CHECK_RET;
|
||||
if (i != (length-1) && (i + start_pos) % 77 == 0) {
|
||||
ret = fprintf_fn(private_data, "\n ");
|
||||
CHECK_RET;
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
#undef CHECK_RET
|
||||
|
||||
/*
|
||||
encode as base64 to a file
|
||||
*/
|
||||
static int base64_encode_f(struct ldb_context *ldb,
|
||||
int (*fprintf_fn)(void *, const char *, ...),
|
||||
void *private_data,
|
||||
const char *buf, int len, int start_pos)
|
||||
{
|
||||
char *b = ldb_base64_encode(ldb, buf, len);
|
||||
int ret;
|
||||
|
||||
if (!b) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = fold_string(fprintf_fn, private_data, b, strlen(b), start_pos);
|
||||
|
||||
talloc_free(b);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
enum ldb_changetype changetype;
|
||||
} ldb_changetypes[] = {
|
||||
{"add", LDB_CHANGETYPE_ADD},
|
||||
{"delete", LDB_CHANGETYPE_DELETE},
|
||||
{"modify", LDB_CHANGETYPE_MODIFY},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
/* this macro is used to handle the return checking on fprintf_fn() */
|
||||
#define CHECK_RET do { if (ret < 0) { talloc_free(mem_ctx); return ret; } total += ret; } while (0)
|
||||
|
||||
/*
|
||||
write to ldif, using a caller supplied write method
|
||||
*/
|
||||
int ldb_ldif_write(struct ldb_context *ldb,
|
||||
int (*fprintf_fn)(void *, const char *, ...),
|
||||
void *private_data,
|
||||
const struct ldb_ldif *ldif)
|
||||
{
|
||||
TALLOC_CTX *mem_ctx;
|
||||
unsigned int i, j;
|
||||
int total=0, ret;
|
||||
const struct ldb_message *msg;
|
||||
|
||||
mem_ctx = talloc_named_const(NULL, 0, "ldb_ldif_write");
|
||||
|
||||
msg = ldif->msg;
|
||||
|
||||
ret = fprintf_fn(private_data, "dn: %s\n", ldb_dn_get_linearized(msg->dn));
|
||||
CHECK_RET;
|
||||
|
||||
if (ldif->changetype != LDB_CHANGETYPE_NONE) {
|
||||
for (i=0;ldb_changetypes[i].name;i++) {
|
||||
if (ldb_changetypes[i].changetype == ldif->changetype) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ldb_changetypes[i].name) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: Invalid ldif changetype %d\n",
|
||||
ldif->changetype);
|
||||
talloc_free(mem_ctx);
|
||||
return -1;
|
||||
}
|
||||
ret = fprintf_fn(private_data, "changetype: %s\n", ldb_changetypes[i].name);
|
||||
CHECK_RET;
|
||||
}
|
||||
|
||||
for (i=0;i<msg->num_elements;i++) {
|
||||
const struct ldb_attrib_handler *h;
|
||||
|
||||
h = ldb_attrib_handler(ldb, msg->elements[i].name);
|
||||
|
||||
if (ldif->changetype == LDB_CHANGETYPE_MODIFY) {
|
||||
switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) {
|
||||
case LDB_FLAG_MOD_ADD:
|
||||
fprintf_fn(private_data, "add: %s\n",
|
||||
msg->elements[i].name);
|
||||
break;
|
||||
case LDB_FLAG_MOD_DELETE:
|
||||
fprintf_fn(private_data, "delete: %s\n",
|
||||
msg->elements[i].name);
|
||||
break;
|
||||
case LDB_FLAG_MOD_REPLACE:
|
||||
fprintf_fn(private_data, "replace: %s\n",
|
||||
msg->elements[i].name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (j=0;j<msg->elements[i].num_values;j++) {
|
||||
struct ldb_val v;
|
||||
ret = h->ldif_write_fn(ldb, mem_ctx, &msg->elements[i].values[j], &v);
|
||||
CHECK_RET;
|
||||
if (ldb_should_b64_encode(&v)) {
|
||||
ret = fprintf_fn(private_data, "%s:: ",
|
||||
msg->elements[i].name);
|
||||
CHECK_RET;
|
||||
ret = base64_encode_f(ldb, fprintf_fn, private_data,
|
||||
(char *)v.data, v.length,
|
||||
strlen(msg->elements[i].name)+3);
|
||||
CHECK_RET;
|
||||
ret = fprintf_fn(private_data, "\n");
|
||||
CHECK_RET;
|
||||
} else {
|
||||
ret = fprintf_fn(private_data, "%s: ", msg->elements[i].name);
|
||||
CHECK_RET;
|
||||
ret = fold_string(fprintf_fn, private_data,
|
||||
(char *)v.data, v.length,
|
||||
strlen(msg->elements[i].name)+2);
|
||||
CHECK_RET;
|
||||
ret = fprintf_fn(private_data, "\n");
|
||||
CHECK_RET;
|
||||
}
|
||||
if (v.data != msg->elements[i].values[j].data) {
|
||||
talloc_free(v.data);
|
||||
}
|
||||
}
|
||||
if (ldif->changetype == LDB_CHANGETYPE_MODIFY) {
|
||||
fprintf_fn(private_data, "-\n");
|
||||
}
|
||||
}
|
||||
ret = fprintf_fn(private_data,"\n");
|
||||
CHECK_RET;
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
#undef CHECK_RET
|
||||
|
||||
|
||||
/*
|
||||
pull a ldif chunk, which is defined as a piece of data ending in \n\n or EOF
|
||||
this routine removes any RFC2849 continuations and comments
|
||||
|
||||
caller frees
|
||||
*/
|
||||
static char *next_chunk(struct ldb_context *ldb,
|
||||
int (*fgetc_fn)(void *), void *private_data)
|
||||
{
|
||||
size_t alloc_size=0, chunk_size = 0;
|
||||
char *chunk = NULL;
|
||||
int c;
|
||||
int in_comment = 0;
|
||||
|
||||
while ((c = fgetc_fn(private_data)) != EOF) {
|
||||
if (chunk_size+1 >= alloc_size) {
|
||||
char *c2;
|
||||
alloc_size += 1024;
|
||||
c2 = talloc_realloc(ldb, chunk, char, alloc_size);
|
||||
if (!c2) {
|
||||
talloc_free(chunk);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
chunk = c2;
|
||||
}
|
||||
|
||||
if (in_comment) {
|
||||
if (c == '\n') {
|
||||
in_comment = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* handle continuation lines - see RFC2849 */
|
||||
if (c == ' ' && chunk_size > 1 && chunk[chunk_size-1] == '\n') {
|
||||
chunk_size--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* chunks are terminated by a double line-feed */
|
||||
if (c == '\n' && chunk_size > 0 && chunk[chunk_size-1] == '\n') {
|
||||
chunk[chunk_size-1] = 0;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
if (c == '#' && (chunk_size == 0 || chunk[chunk_size-1] == '\n')) {
|
||||
in_comment = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ignore leading blank lines */
|
||||
if (chunk_size == 0 && c == '\n') {
|
||||
continue;
|
||||
}
|
||||
|
||||
chunk[chunk_size++] = c;
|
||||
}
|
||||
|
||||
if (chunk) {
|
||||
chunk[chunk_size] = 0;
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
|
||||
/* simple ldif attribute parser */
|
||||
static int next_attr(void *mem_ctx, char **s, const char **attr, struct ldb_val *value)
|
||||
{
|
||||
char *p;
|
||||
int base64_encoded = 0;
|
||||
int binary_file = 0;
|
||||
|
||||
if (strncmp(*s, "-\n", 2) == 0) {
|
||||
value->length = 0;
|
||||
*attr = "-";
|
||||
*s += 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = strchr(*s, ':');
|
||||
if (!p) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*p++ = 0;
|
||||
|
||||
if (*p == ':') {
|
||||
base64_encoded = 1;
|
||||
p++;
|
||||
}
|
||||
|
||||
if (*p == '<') {
|
||||
binary_file = 1;
|
||||
p++;
|
||||
}
|
||||
|
||||
*attr = *s;
|
||||
|
||||
while (*p == ' ' || *p == '\t') {
|
||||
p++;
|
||||
}
|
||||
|
||||
value->data = (uint8_t *)p;
|
||||
|
||||
p = strchr(p, '\n');
|
||||
|
||||
if (!p) {
|
||||
value->length = strlen((char *)value->data);
|
||||
*s = ((char *)value->data) + value->length;
|
||||
} else {
|
||||
value->length = p - (char *)value->data;
|
||||
*s = p+1;
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
if (base64_encoded) {
|
||||
int len = ldb_base64_decode((char *)value->data);
|
||||
if (len == -1) {
|
||||
/* it wasn't valid base64 data */
|
||||
return -1;
|
||||
}
|
||||
value->length = len;
|
||||
}
|
||||
|
||||
if (binary_file) {
|
||||
int len = ldb_read_data_file(mem_ctx, value);
|
||||
if (len == -1) {
|
||||
/* an error occured hile trying to retrieve the file */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
free a message from a ldif_read
|
||||
*/
|
||||
void ldb_ldif_read_free(struct ldb_context *ldb, struct ldb_ldif *ldif)
|
||||
{
|
||||
talloc_free(ldif);
|
||||
}
|
||||
|
||||
/*
|
||||
read from a LDIF source, creating a ldb_message
|
||||
*/
|
||||
struct ldb_ldif *ldb_ldif_read(struct ldb_context *ldb,
|
||||
int (*fgetc_fn)(void *), void *private_data)
|
||||
{
|
||||
struct ldb_ldif *ldif;
|
||||
struct ldb_message *msg;
|
||||
const char *attr=NULL;
|
||||
char *chunk=NULL, *s;
|
||||
struct ldb_val value;
|
||||
unsigned flags = 0;
|
||||
|
||||
value.data = NULL;
|
||||
|
||||
ldif = talloc(ldb, struct ldb_ldif);
|
||||
if (!ldif) return NULL;
|
||||
|
||||
ldif->msg = talloc(ldif, struct ldb_message);
|
||||
if (ldif->msg == NULL) {
|
||||
talloc_free(ldif);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ldif->changetype = LDB_CHANGETYPE_NONE;
|
||||
msg = ldif->msg;
|
||||
|
||||
msg->dn = NULL;
|
||||
msg->elements = NULL;
|
||||
msg->num_elements = 0;
|
||||
msg->private_data = NULL;
|
||||
|
||||
chunk = next_chunk(ldb, fgetc_fn, private_data);
|
||||
if (!chunk) {
|
||||
goto failed;
|
||||
}
|
||||
talloc_steal(ldif, chunk);
|
||||
|
||||
msg->private_data = chunk;
|
||||
s = chunk;
|
||||
|
||||
if (next_attr(ldif, &s, &attr, &value) != 0) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* first line must be a dn */
|
||||
if (ldb_attr_cmp(attr, "dn") != 0) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: First line of ldif must be a dn not '%s'\n",
|
||||
attr);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
msg->dn = ldb_dn_new(msg, ldb, (char *)value.data);
|
||||
|
||||
if ( ! ldb_dn_validate(msg->dn)) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: Unable to parse dn '%s'\n",
|
||||
value.data);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
while (next_attr(ldif, &s, &attr, &value) == 0) {
|
||||
const struct ldb_attrib_handler *h;
|
||||
struct ldb_message_element *el;
|
||||
int ret, empty = 0;
|
||||
|
||||
if (ldb_attr_cmp(attr, "changetype") == 0) {
|
||||
int i;
|
||||
for (i=0;ldb_changetypes[i].name;i++) {
|
||||
if (ldb_attr_cmp((char *)value.data, ldb_changetypes[i].name) == 0) {
|
||||
ldif->changetype = ldb_changetypes[i].changetype;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ldb_changetypes[i].name) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR,
|
||||
"Error: Bad ldif changetype '%s'\n",(char *)value.data);
|
||||
}
|
||||
flags = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ldb_attr_cmp(attr, "add") == 0) {
|
||||
flags = LDB_FLAG_MOD_ADD;
|
||||
empty = 1;
|
||||
}
|
||||
if (ldb_attr_cmp(attr, "delete") == 0) {
|
||||
flags = LDB_FLAG_MOD_DELETE;
|
||||
empty = 1;
|
||||
}
|
||||
if (ldb_attr_cmp(attr, "replace") == 0) {
|
||||
flags = LDB_FLAG_MOD_REPLACE;
|
||||
empty = 1;
|
||||
}
|
||||
if (ldb_attr_cmp(attr, "-") == 0) {
|
||||
flags = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty) {
|
||||
if (ldb_msg_add_empty(msg, (char *)value.data, flags, NULL) != 0) {
|
||||
goto failed;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
el = &msg->elements[msg->num_elements-1];
|
||||
|
||||
h = ldb_attrib_handler(ldb, attr);
|
||||
|
||||
if (msg->num_elements > 0 && ldb_attr_cmp(attr, el->name) == 0 &&
|
||||
flags == el->flags) {
|
||||
/* its a continuation */
|
||||
el->values =
|
||||
talloc_realloc(msg->elements, el->values,
|
||||
struct ldb_val, el->num_values+1);
|
||||
if (!el->values) {
|
||||
goto failed;
|
||||
}
|
||||
ret = h->ldif_read_fn(ldb, ldif, &value, &el->values[el->num_values]);
|
||||
if (ret != 0) {
|
||||
goto failed;
|
||||
}
|
||||
if (value.length == 0) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR,
|
||||
"Error: Attribute value cannot be empty for attribute '%s'\n", el->name);
|
||||
goto failed;
|
||||
}
|
||||
if (value.data != el->values[el->num_values].data) {
|
||||
talloc_steal(el->values, el->values[el->num_values].data);
|
||||
}
|
||||
el->num_values++;
|
||||
} else {
|
||||
/* its a new attribute */
|
||||
msg->elements = talloc_realloc(ldif, msg->elements,
|
||||
struct ldb_message_element,
|
||||
msg->num_elements+1);
|
||||
if (!msg->elements) {
|
||||
goto failed;
|
||||
}
|
||||
el = &msg->elements[msg->num_elements];
|
||||
el->flags = flags;
|
||||
el->name = talloc_strdup(msg->elements, attr);
|
||||
el->values = talloc(msg->elements, struct ldb_val);
|
||||
if (!el->values || !el->name) {
|
||||
goto failed;
|
||||
}
|
||||
el->num_values = 1;
|
||||
ret = h->ldif_read_fn(ldb, ldif, &value, &el->values[0]);
|
||||
if (ret != 0) {
|
||||
goto failed;
|
||||
}
|
||||
if (value.data != el->values[0].data) {
|
||||
talloc_steal(el->values, el->values[0].data);
|
||||
}
|
||||
msg->num_elements++;
|
||||
}
|
||||
}
|
||||
|
||||
return ldif;
|
||||
|
||||
failed:
|
||||
talloc_free(ldif);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
a wrapper around ldif_read() for reading from FILE*
|
||||
*/
|
||||
struct ldif_read_file_state {
|
||||
FILE *f;
|
||||
};
|
||||
|
||||
static int fgetc_file(void *private_data)
|
||||
{
|
||||
struct ldif_read_file_state *state =
|
||||
(struct ldif_read_file_state *)private_data;
|
||||
return fgetc(state->f);
|
||||
}
|
||||
|
||||
struct ldb_ldif *ldb_ldif_read_file(struct ldb_context *ldb, FILE *f)
|
||||
{
|
||||
struct ldif_read_file_state state;
|
||||
state.f = f;
|
||||
return ldb_ldif_read(ldb, fgetc_file, &state);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
a wrapper around ldif_read() for reading from const char*
|
||||
*/
|
||||
struct ldif_read_string_state {
|
||||
const char *s;
|
||||
};
|
||||
|
||||
static int fgetc_string(void *private_data)
|
||||
{
|
||||
struct ldif_read_string_state *state =
|
||||
(struct ldif_read_string_state *)private_data;
|
||||
if (state->s[0] != 0) {
|
||||
return *state->s++;
|
||||
}
|
||||
return EOF;
|
||||
}
|
||||
|
||||
struct ldb_ldif *ldb_ldif_read_string(struct ldb_context *ldb, const char **s)
|
||||
{
|
||||
struct ldif_read_string_state state;
|
||||
struct ldb_ldif *ldif;
|
||||
state.s = *s;
|
||||
ldif = ldb_ldif_read(ldb, fgetc_string, &state);
|
||||
*s = state.s;
|
||||
return ldif;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
wrapper around ldif_write() for a file
|
||||
*/
|
||||
struct ldif_write_file_state {
|
||||
FILE *f;
|
||||
};
|
||||
|
||||
static int fprintf_file(void *private_data, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 3);
|
||||
|
||||
static int fprintf_file(void *private_data, const char *fmt, ...)
|
||||
{
|
||||
struct ldif_write_file_state *state =
|
||||
(struct ldif_write_file_state *)private_data;
|
||||
int ret;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = vfprintf(state->f, fmt, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ldb_ldif_write_file(struct ldb_context *ldb, FILE *f, const struct ldb_ldif *ldif)
|
||||
{
|
||||
struct ldif_write_file_state state;
|
||||
state.f = f;
|
||||
return ldb_ldif_write(ldb, fprintf_file, &state, ldif);
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004-2005
|
||||
Copyright (C) Simo Sorce 2005
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb expression matching
|
||||
*
|
||||
* Description: ldb expression matching
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
/*
|
||||
check if the scope matches in a search result
|
||||
*/
|
||||
static int ldb_match_scope(struct ldb_context *ldb,
|
||||
struct ldb_dn *base,
|
||||
struct ldb_dn *dn,
|
||||
enum ldb_scope scope)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (base == NULL || dn == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (scope) {
|
||||
case LDB_SCOPE_BASE:
|
||||
if (ldb_dn_compare(base, dn) == 0) {
|
||||
ret = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case LDB_SCOPE_ONELEVEL:
|
||||
if (ldb_dn_get_comp_num(dn) == (ldb_dn_get_comp_num(base) + 1)) {
|
||||
if (ldb_dn_compare_base(base, dn) == 0) {
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LDB_SCOPE_SUBTREE:
|
||||
default:
|
||||
if (ldb_dn_compare_base(base, dn) == 0) {
|
||||
ret = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
match if node is present
|
||||
*/
|
||||
static int ldb_match_present(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg,
|
||||
const struct ldb_parse_tree *tree,
|
||||
enum ldb_scope scope)
|
||||
{
|
||||
if (ldb_attr_dn(tree->u.present.attr) == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ldb_msg_find_element(msg, tree->u.present.attr)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ldb_match_comparison(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg,
|
||||
const struct ldb_parse_tree *tree,
|
||||
enum ldb_scope scope,
|
||||
enum ldb_parse_op comp_op)
|
||||
{
|
||||
unsigned int i;
|
||||
struct ldb_message_element *el;
|
||||
const struct ldb_attrib_handler *h;
|
||||
int ret;
|
||||
|
||||
/* FIXME: APPROX comparison not handled yet */
|
||||
if (comp_op == LDB_OP_APPROX) return 0;
|
||||
|
||||
el = ldb_msg_find_element(msg, tree->u.comparison.attr);
|
||||
if (el == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
h = ldb_attrib_handler(ldb, el->name);
|
||||
|
||||
for (i = 0; i < el->num_values; i++) {
|
||||
ret = h->comparison_fn(ldb, ldb, &el->values[i], &tree->u.comparison.value);
|
||||
|
||||
if (ret == 0) {
|
||||
return 1;
|
||||
}
|
||||
if (ret > 0 && comp_op == LDB_OP_GREATER) {
|
||||
return 1;
|
||||
}
|
||||
if (ret < 0 && comp_op == LDB_OP_LESS) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
match a simple leaf node
|
||||
*/
|
||||
static int ldb_match_equality(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg,
|
||||
const struct ldb_parse_tree *tree,
|
||||
enum ldb_scope scope)
|
||||
{
|
||||
unsigned int i;
|
||||
struct ldb_message_element *el;
|
||||
const struct ldb_attrib_handler *h;
|
||||
struct ldb_dn *valuedn;
|
||||
int ret;
|
||||
|
||||
if (ldb_attr_dn(tree->u.equality.attr) == 0) {
|
||||
valuedn = ldb_dn_new(ldb, ldb, (char *)tree->u.equality.value.data);
|
||||
if (valuedn == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = ldb_dn_compare(msg->dn, valuedn);
|
||||
|
||||
talloc_free(valuedn);
|
||||
|
||||
if (ret == 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: handle the "*" case derived from an extended search
|
||||
operation without the attibute type defined */
|
||||
el = ldb_msg_find_element(msg, tree->u.equality.attr);
|
||||
if (el == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
h = ldb_attrib_handler(ldb, el->name);
|
||||
|
||||
for (i=0;i<el->num_values;i++) {
|
||||
if (h->comparison_fn(ldb, ldb, &tree->u.equality.value,
|
||||
&el->values[i]) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ldb_wildcard_compare(struct ldb_context *ldb,
|
||||
const struct ldb_parse_tree *tree,
|
||||
const struct ldb_val value)
|
||||
{
|
||||
const struct ldb_attrib_handler *h;
|
||||
struct ldb_val val;
|
||||
struct ldb_val cnk;
|
||||
struct ldb_val *chunk;
|
||||
char *p, *g;
|
||||
uint8_t *save_p = NULL;
|
||||
int c = 0;
|
||||
|
||||
h = ldb_attrib_handler(ldb, tree->u.substring.attr);
|
||||
|
||||
if(h->canonicalise_fn(ldb, ldb, &value, &val) != 0)
|
||||
return -1;
|
||||
|
||||
save_p = val.data;
|
||||
cnk.data = NULL;
|
||||
|
||||
if ( ! tree->u.substring.start_with_wildcard ) {
|
||||
|
||||
chunk = tree->u.substring.chunks[c];
|
||||
if(h->canonicalise_fn(ldb, ldb, chunk, &cnk) != 0) goto failed;
|
||||
|
||||
/* This deals with wildcard prefix searches on binary attributes (eg objectGUID) */
|
||||
if (cnk.length > val.length) {
|
||||
goto failed;
|
||||
}
|
||||
if (memcmp((char *)val.data, (char *)cnk.data, cnk.length) != 0) goto failed;
|
||||
val.length -= cnk.length;
|
||||
val.data += cnk.length;
|
||||
c++;
|
||||
talloc_free(cnk.data);
|
||||
cnk.data = NULL;
|
||||
}
|
||||
|
||||
while (tree->u.substring.chunks[c]) {
|
||||
|
||||
chunk = tree->u.substring.chunks[c];
|
||||
if(h->canonicalise_fn(ldb, ldb, chunk, &cnk) != 0) goto failed;
|
||||
|
||||
/* FIXME: case of embedded nulls */
|
||||
p = strstr((char *)val.data, (char *)cnk.data);
|
||||
if (p == NULL) goto failed;
|
||||
if ( (! tree->u.substring.chunks[c + 1]) && (! tree->u.substring.end_with_wildcard) ) {
|
||||
do { /* greedy */
|
||||
g = strstr((char *)p + cnk.length, (char *)cnk.data);
|
||||
if (g) p = g;
|
||||
} while(g);
|
||||
}
|
||||
val.length = val.length - (p - (char *)(val.data)) - cnk.length;
|
||||
val.data = (uint8_t *)(p + cnk.length);
|
||||
c++;
|
||||
talloc_free(cnk.data);
|
||||
cnk.data = NULL;
|
||||
}
|
||||
|
||||
if ( (! tree->u.substring.end_with_wildcard) && (*(val.data) != 0) ) goto failed; /* last chunk have not reached end of string */
|
||||
talloc_free(save_p);
|
||||
return 1;
|
||||
|
||||
failed:
|
||||
talloc_free(save_p);
|
||||
talloc_free(cnk.data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
match a simple leaf node
|
||||
*/
|
||||
static int ldb_match_substring(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg,
|
||||
const struct ldb_parse_tree *tree,
|
||||
enum ldb_scope scope)
|
||||
{
|
||||
unsigned int i;
|
||||
struct ldb_message_element *el;
|
||||
|
||||
el = ldb_msg_find_element(msg, tree->u.substring.attr);
|
||||
if (el == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < el->num_values; i++) {
|
||||
if (ldb_wildcard_compare(ldb, tree, el->values[i]) == 1) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
bitwise-and comparator
|
||||
*/
|
||||
static int ldb_comparator_and(const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
uint64_t i1, i2;
|
||||
i1 = strtoull((char *)v1->data, NULL, 0);
|
||||
i2 = strtoull((char *)v2->data, NULL, 0);
|
||||
return ((i1 & i2) == i2);
|
||||
}
|
||||
|
||||
/*
|
||||
bitwise-or comparator
|
||||
*/
|
||||
static int ldb_comparator_or(const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
uint64_t i1, i2;
|
||||
i1 = strtoull((char *)v1->data, NULL, 0);
|
||||
i2 = strtoull((char *)v2->data, NULL, 0);
|
||||
return ((i1 & i2) != 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
extended match, handles things like bitops
|
||||
*/
|
||||
static int ldb_match_extended(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg,
|
||||
const struct ldb_parse_tree *tree,
|
||||
enum ldb_scope scope)
|
||||
{
|
||||
int i;
|
||||
const struct {
|
||||
const char *oid;
|
||||
int (*comparator)(const struct ldb_val *, const struct ldb_val *);
|
||||
} rules[] = {
|
||||
{ LDB_OID_COMPARATOR_AND, ldb_comparator_and},
|
||||
{ LDB_OID_COMPARATOR_OR, ldb_comparator_or}
|
||||
};
|
||||
int (*comp)(const struct ldb_val *, const struct ldb_val *) = NULL;
|
||||
struct ldb_message_element *el;
|
||||
|
||||
if (tree->u.extended.dnAttributes) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: dnAttributes extended match not supported yet");
|
||||
return -1;
|
||||
}
|
||||
if (tree->u.extended.rule_id == NULL) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: no-rule extended matches not supported yet");
|
||||
return -1;
|
||||
}
|
||||
if (tree->u.extended.attr == NULL) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: no-attribute extended matches not supported yet");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i=0;i<ARRAY_SIZE(rules);i++) {
|
||||
if (strcmp(rules[i].oid, tree->u.extended.rule_id) == 0) {
|
||||
comp = rules[i].comparator;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (comp == NULL) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: unknown extended rule_id %s\n",
|
||||
tree->u.extended.rule_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* find the message element */
|
||||
el = ldb_msg_find_element(msg, tree->u.extended.attr);
|
||||
if (el == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i=0;i<el->num_values;i++) {
|
||||
int ret = comp(&el->values[i], &tree->u.extended.value);
|
||||
if (ret == -1 || ret == 1) return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
return 0 if the given parse tree matches the given message. Assumes
|
||||
the message is in sorted order
|
||||
|
||||
return 1 if it matches, and 0 if it doesn't match
|
||||
|
||||
this is a recursive function, and does short-circuit evaluation
|
||||
*/
|
||||
static int ldb_match_message(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg,
|
||||
const struct ldb_parse_tree *tree,
|
||||
enum ldb_scope scope)
|
||||
{
|
||||
unsigned int i;
|
||||
int v;
|
||||
|
||||
switch (tree->operation) {
|
||||
case LDB_OP_AND:
|
||||
for (i=0;i<tree->u.list.num_elements;i++) {
|
||||
v = ldb_match_message(ldb, msg, tree->u.list.elements[i], scope);
|
||||
if (!v) return 0;
|
||||
}
|
||||
return 1;
|
||||
|
||||
case LDB_OP_OR:
|
||||
for (i=0;i<tree->u.list.num_elements;i++) {
|
||||
v = ldb_match_message(ldb, msg, tree->u.list.elements[i], scope);
|
||||
if (v) return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case LDB_OP_NOT:
|
||||
return ! ldb_match_message(ldb, msg, tree->u.isnot.child, scope);
|
||||
|
||||
case LDB_OP_EQUALITY:
|
||||
return ldb_match_equality(ldb, msg, tree, scope);
|
||||
|
||||
case LDB_OP_SUBSTRING:
|
||||
return ldb_match_substring(ldb, msg, tree, scope);
|
||||
|
||||
case LDB_OP_GREATER:
|
||||
return ldb_match_comparison(ldb, msg, tree, scope, LDB_OP_GREATER);
|
||||
|
||||
case LDB_OP_LESS:
|
||||
return ldb_match_comparison(ldb, msg, tree, scope, LDB_OP_LESS);
|
||||
|
||||
case LDB_OP_PRESENT:
|
||||
return ldb_match_present(ldb, msg, tree, scope);
|
||||
|
||||
case LDB_OP_APPROX:
|
||||
return ldb_match_comparison(ldb, msg, tree, scope, LDB_OP_APPROX);
|
||||
|
||||
case LDB_OP_EXTENDED:
|
||||
return ldb_match_extended(ldb, msg, tree, scope);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ldb_match_msg(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg,
|
||||
const struct ldb_parse_tree *tree,
|
||||
struct ldb_dn *base,
|
||||
enum ldb_scope scope)
|
||||
{
|
||||
if ( ! ldb_match_scope(ldb, base, msg->dn, scope) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ldb_match_message(ldb, msg, tree, scope);
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Simo Sorce 2004
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb modules core
|
||||
*
|
||||
* Description: core modules routines
|
||||
*
|
||||
* Author: Simo Sorce
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
#if (_SAMBA_BUILD_ >= 4)
|
||||
#include "build.h"
|
||||
#include "dynconfig.h"
|
||||
#endif
|
||||
|
||||
#define LDB_MODULE_PREFIX "modules:"
|
||||
#define LDB_MODULE_PREFIX_LEN 8
|
||||
|
||||
static char *ldb_modules_strdup_no_spaces(TALLOC_CTX *mem_ctx, const char *string)
|
||||
{
|
||||
int i, len;
|
||||
char *trimmed;
|
||||
|
||||
trimmed = talloc_strdup(mem_ctx, string);
|
||||
if (!trimmed) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len = strlen(trimmed);
|
||||
for (i = 0; trimmed[i] != '\0'; i++) {
|
||||
switch (trimmed[i]) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
memmove(&trimmed[i], &trimmed[i + 1], len -i -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
|
||||
/* modules are called in inverse order on the stack.
|
||||
Lets place them as an admin would think the right order is.
|
||||
Modules order is important */
|
||||
const char **ldb_modules_list_from_string(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *string)
|
||||
{
|
||||
char **modules = NULL;
|
||||
const char **m;
|
||||
char *modstr, *p;
|
||||
int i;
|
||||
|
||||
/* spaces not admitted */
|
||||
modstr = ldb_modules_strdup_no_spaces(mem_ctx, string);
|
||||
if ( ! modstr) {
|
||||
ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_strdup_no_spaces()\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
modules = talloc_realloc(mem_ctx, modules, char *, 2);
|
||||
if ( ! modules ) {
|
||||
ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_list_from_string()\n");
|
||||
talloc_free(modstr);
|
||||
return NULL;
|
||||
}
|
||||
talloc_steal(modules, modstr);
|
||||
|
||||
i = 0;
|
||||
/* The str*r*chr walks backwards: This is how we get the inverse order mentioned above */
|
||||
while ((p = strrchr(modstr, ',')) != NULL) {
|
||||
*p = '\0';
|
||||
p++;
|
||||
modules[i] = p;
|
||||
|
||||
i++;
|
||||
modules = talloc_realloc(mem_ctx, modules, char *, i + 2);
|
||||
if ( ! modules ) {
|
||||
ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_list_from_string()\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
modules[i] = modstr;
|
||||
|
||||
modules[i + 1] = NULL;
|
||||
|
||||
m = (const char **)modules;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
static struct ops_list_entry {
|
||||
const struct ldb_module_ops *ops;
|
||||
struct ops_list_entry *next;
|
||||
} *registered_modules = NULL;
|
||||
|
||||
static const struct ldb_module_ops *ldb_find_module_ops(const char *name)
|
||||
{
|
||||
struct ops_list_entry *e;
|
||||
|
||||
for (e = registered_modules; e; e = e->next) {
|
||||
if (strcmp(e->ops->name, name) == 0)
|
||||
return e->ops;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifndef STATIC_ldb_MODULES
|
||||
|
||||
#ifdef HAVE_LDB_LDAP
|
||||
#define LDAP_INIT ldb_ldap_init,
|
||||
#else
|
||||
#define LDAP_INIT
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LDB_SQLITE3
|
||||
#define SQLITE3_INIT ldb_sqlite3_init,
|
||||
#else
|
||||
#define SQLITE3_INIT
|
||||
#endif
|
||||
|
||||
#define STATIC_ldb_MODULES \
|
||||
{ \
|
||||
LDAP_INIT \
|
||||
SQLITE3_INIT \
|
||||
ldb_tdb_init, \
|
||||
ldb_operational_init, \
|
||||
ldb_rdn_name_init, \
|
||||
ldb_objectclass_init, \
|
||||
ldb_paged_results_init, \
|
||||
ldb_sort_init, \
|
||||
ldb_asq_init, \
|
||||
NULL \
|
||||
}
|
||||
#endif
|
||||
|
||||
int ldb_global_init(void)
|
||||
{
|
||||
static int (*static_init_fns[])(void) = STATIC_ldb_MODULES;
|
||||
|
||||
static int initialized = 0;
|
||||
int ret = 0, i;
|
||||
|
||||
if (initialized)
|
||||
return 0;
|
||||
|
||||
initialized = 1;
|
||||
|
||||
for (i = 0; static_init_fns[i]; i++) {
|
||||
if (static_init_fns[i]() == -1)
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ldb_register_module(const struct ldb_module_ops *ops)
|
||||
{
|
||||
struct ops_list_entry *entry = talloc(talloc_autofree_context(), struct ops_list_entry);
|
||||
|
||||
if (ldb_find_module_ops(ops->name) != NULL)
|
||||
return -1;
|
||||
|
||||
if (entry == NULL)
|
||||
return -1;
|
||||
|
||||
entry->ops = ops;
|
||||
entry->next = registered_modules;
|
||||
registered_modules = entry;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ldb_try_load_dso(struct ldb_context *ldb, const char *name)
|
||||
{
|
||||
char *path;
|
||||
void *handle;
|
||||
int (*init_fn) (void);
|
||||
char *modulesdir;
|
||||
|
||||
#ifdef HAVE_DLOPEN
|
||||
if (getenv("LD_LDB_MODULE_PATH") != NULL) {
|
||||
modulesdir = talloc_strdup(ldb, getenv("LD_LDB_MODULE_PATH"));
|
||||
} else {
|
||||
#ifdef _SAMBA_BUILD_
|
||||
modulesdir = talloc_asprintf(ldb, "%s/ldb", dyn_MODULESDIR);
|
||||
#else
|
||||
modulesdir = talloc_strdup(ldb, MODULESDIR);
|
||||
#endif
|
||||
}
|
||||
|
||||
path = talloc_asprintf(ldb, "%s/%s.%s", modulesdir, name, SHLIBEXT);
|
||||
|
||||
talloc_free(modulesdir);
|
||||
|
||||
ldb_debug(ldb, LDB_DEBUG_TRACE, "trying to load %s from %s\n", name, path);
|
||||
|
||||
handle = dlopen(path, RTLD_NOW);
|
||||
if (handle == NULL) {
|
||||
ldb_debug(ldb, LDB_DEBUG_WARNING, "unable to load %s from %s: %s\n", name, path, dlerror());
|
||||
return -1;
|
||||
}
|
||||
|
||||
init_fn = (int (*)(void))dlsym(handle, "init_module");
|
||||
|
||||
if (init_fn == NULL) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "no symbol `init_module' found in %s: %s\n", path, dlerror());
|
||||
return -1;
|
||||
}
|
||||
|
||||
talloc_free(path);
|
||||
|
||||
return init_fn();
|
||||
#else
|
||||
ldb_debug(ldb, LDB_DEBUG_TRACE, "no dlopen() - not trying to load %s module\n", name);
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int ldb_load_modules_list(struct ldb_context *ldb, const char **module_list, struct ldb_module *backend, struct ldb_module **out)
|
||||
{
|
||||
struct ldb_module *module;
|
||||
int i;
|
||||
|
||||
module = backend;
|
||||
|
||||
for (i = 0; module_list[i] != NULL; i++) {
|
||||
struct ldb_module *current;
|
||||
const struct ldb_module_ops *ops;
|
||||
|
||||
ops = ldb_find_module_ops(module_list[i]);
|
||||
if (ops == NULL) {
|
||||
if (ldb_try_load_dso(ldb, module_list[i]) == 0) {
|
||||
ops = ldb_find_module_ops(module_list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (ops == NULL) {
|
||||
ldb_debug(ldb, LDB_DEBUG_WARNING, "WARNING: Module [%s] not found\n",
|
||||
module_list[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
current = talloc_zero(ldb, struct ldb_module);
|
||||
if (current == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
talloc_set_name(current, "ldb_module: %s", module_list[i]);
|
||||
|
||||
current->ldb = ldb;
|
||||
current->ops = ops;
|
||||
|
||||
DLIST_ADD(module, current);
|
||||
}
|
||||
*out = module;
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
int ldb_init_module_chain(struct ldb_context *ldb, struct ldb_module *module)
|
||||
{
|
||||
while (module && module->ops->init_context == NULL)
|
||||
module = module->next;
|
||||
|
||||
if (module && module->ops->init_context &&
|
||||
module->ops->init_context(module) != LDB_SUCCESS) {
|
||||
ldb_debug(ldb, LDB_DEBUG_FATAL, "module initialization failed\n");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
int ldb_load_modules(struct ldb_context *ldb, const char *options[])
|
||||
{
|
||||
const char **modules = NULL;
|
||||
int i;
|
||||
int ret;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(ldb);
|
||||
if (!mem_ctx) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
/* find out which modules we are requested to activate */
|
||||
|
||||
/* check if we have a custom module list passd as ldb option */
|
||||
if (options) {
|
||||
for (i = 0; options[i] != NULL; i++) {
|
||||
if (strncmp(options[i], LDB_MODULE_PREFIX, LDB_MODULE_PREFIX_LEN) == 0) {
|
||||
modules = ldb_modules_list_from_string(ldb, mem_ctx, &options[i][LDB_MODULE_PREFIX_LEN]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if not overloaded by options and the backend is not ldap try to load the modules list from ldb */
|
||||
if ((modules == NULL) && (strcmp("ldap", ldb->modules->ops->name) != 0)) {
|
||||
const char * const attrs[] = { "@LIST" , NULL};
|
||||
struct ldb_result *res = NULL;
|
||||
struct ldb_dn *mods_dn;
|
||||
|
||||
mods_dn = ldb_dn_new(mem_ctx, ldb, "@MODULES");
|
||||
if (mods_dn == NULL) {
|
||||
talloc_free(mem_ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ldb_search(ldb, mods_dn, LDB_SCOPE_BASE, "", attrs, &res);
|
||||
talloc_steal(mods_dn, res);
|
||||
if (ret == LDB_SUCCESS && (res->count == 0 || res->msgs[0]->num_elements == 0)) {
|
||||
ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db\n");
|
||||
} else {
|
||||
if (ret != LDB_SUCCESS) {
|
||||
ldb_debug(ldb, LDB_DEBUG_FATAL, "ldb error (%s) occurred searching for modules, bailing out\n", ldb_errstring(ldb));
|
||||
talloc_free(mem_ctx);
|
||||
return -1;
|
||||
}
|
||||
if (res->count > 1) {
|
||||
ldb_debug(ldb, LDB_DEBUG_FATAL, "Too many records found (%d), bailing out\n", res->count);
|
||||
talloc_free(mem_ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
modules = ldb_modules_list_from_string(ldb, mem_ctx,
|
||||
(const char *)res->msgs[0]->elements[0].values[0].data);
|
||||
|
||||
}
|
||||
|
||||
talloc_free(mods_dn);
|
||||
}
|
||||
|
||||
if (modules != NULL) {
|
||||
ret = ldb_load_modules_list(ldb, modules, ldb->modules, &ldb->modules);
|
||||
talloc_free(modules);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ldb_debug(ldb, LDB_DEBUG_TRACE, "No modules specified for this database\n");
|
||||
}
|
||||
|
||||
return ldb_init_module_chain(ldb, ldb->modules);
|
||||
}
|
||||
|
||||
/*
|
||||
by using this we allow ldb modules to only implement the functions they care about,
|
||||
which makes writing a module simpler, and makes it more likely to keep working
|
||||
when ldb is extended
|
||||
*/
|
||||
#define FIND_OP(module, op) do { \
|
||||
struct ldb_context *ldb = module->ldb; \
|
||||
module = module->next; \
|
||||
while (module && module->ops->op == NULL) module = module->next; \
|
||||
if (module == NULL) { \
|
||||
ldb_asprintf_errstring(ldb, "Unable to find backend operation for " #op ); \
|
||||
return LDB_ERR_OPERATIONS_ERROR; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*
|
||||
helper functions to call the next module in chain
|
||||
*/
|
||||
|
||||
int ldb_next_request(struct ldb_module *module, struct ldb_request *request)
|
||||
{
|
||||
switch (request->operation) {
|
||||
case LDB_SEARCH:
|
||||
FIND_OP(module, search);
|
||||
return module->ops->search(module, request);
|
||||
case LDB_ADD:
|
||||
FIND_OP(module, add);
|
||||
return module->ops->add(module, request);
|
||||
case LDB_MODIFY:
|
||||
FIND_OP(module, modify);
|
||||
return module->ops->modify(module, request);
|
||||
case LDB_DELETE:
|
||||
FIND_OP(module, del);
|
||||
return module->ops->del(module, request);
|
||||
case LDB_RENAME:
|
||||
FIND_OP(module, rename);
|
||||
return module->ops->rename(module, request);
|
||||
case LDB_SEQUENCE_NUMBER:
|
||||
FIND_OP(module, sequence_number);
|
||||
return module->ops->sequence_number(module, request);
|
||||
default:
|
||||
FIND_OP(module, request);
|
||||
return module->ops->request(module, request);
|
||||
}
|
||||
}
|
||||
|
||||
int ldb_next_init(struct ldb_module *module)
|
||||
{
|
||||
/* init is different in that it is not an error if modules
|
||||
* do not require initialization */
|
||||
|
||||
module = module->next;
|
||||
|
||||
while (module && module->ops->init_context == NULL)
|
||||
module = module->next;
|
||||
|
||||
if (module == NULL)
|
||||
return LDB_SUCCESS;
|
||||
|
||||
return module->ops->init_context(module);
|
||||
}
|
||||
|
||||
int ldb_next_start_trans(struct ldb_module *module)
|
||||
{
|
||||
FIND_OP(module, start_transaction);
|
||||
return module->ops->start_transaction(module);
|
||||
}
|
||||
|
||||
int ldb_next_end_trans(struct ldb_module *module)
|
||||
{
|
||||
FIND_OP(module, end_transaction);
|
||||
return module->ops->end_transaction(module);
|
||||
}
|
||||
|
||||
int ldb_next_del_trans(struct ldb_module *module)
|
||||
{
|
||||
FIND_OP(module, del_transaction);
|
||||
return module->ops->del_transaction(module);
|
||||
}
|
||||
@@ -0,0 +1,846 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb message component utility functions
|
||||
*
|
||||
* Description: functions for manipulating ldb_message structures
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
/*
|
||||
create a new ldb_message in a given memory context (NULL for top level)
|
||||
*/
|
||||
struct ldb_message *ldb_msg_new(void *mem_ctx)
|
||||
{
|
||||
return talloc_zero(mem_ctx, struct ldb_message);
|
||||
}
|
||||
|
||||
/*
|
||||
find an element in a message by attribute name
|
||||
*/
|
||||
struct ldb_message_element *ldb_msg_find_element(const struct ldb_message *msg,
|
||||
const char *attr_name)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i=0;i<msg->num_elements;i++) {
|
||||
if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
|
||||
return &msg->elements[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
see if two ldb_val structures contain exactly the same data
|
||||
return 1 for a match, 0 for a mis-match
|
||||
*/
|
||||
int ldb_val_equal_exact(const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
if (v1->length != v2->length) return 0;
|
||||
|
||||
if (v1->length == 0) return 1;
|
||||
|
||||
if (memcmp(v1->data, v2->data, v1->length) == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
find a value in an element
|
||||
assumes case sensitive comparison
|
||||
*/
|
||||
struct ldb_val *ldb_msg_find_val(const struct ldb_message_element *el,
|
||||
struct ldb_val *val)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i=0;i<el->num_values;i++) {
|
||||
if (ldb_val_equal_exact(val, &el->values[i])) {
|
||||
return &el->values[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
duplicate a ldb_val structure
|
||||
*/
|
||||
struct ldb_val ldb_val_dup(void *mem_ctx, const struct ldb_val *v)
|
||||
{
|
||||
struct ldb_val v2;
|
||||
v2.length = v->length;
|
||||
if (v->data == NULL) {
|
||||
v2.data = NULL;
|
||||
return v2;
|
||||
}
|
||||
|
||||
/* the +1 is to cope with buggy C library routines like strndup
|
||||
that look one byte beyond */
|
||||
v2.data = talloc_array(mem_ctx, uint8_t, v->length+1);
|
||||
if (!v2.data) {
|
||||
v2.length = 0;
|
||||
return v2;
|
||||
}
|
||||
|
||||
memcpy(v2.data, v->data, v->length);
|
||||
((char *)v2.data)[v->length] = 0;
|
||||
return v2;
|
||||
}
|
||||
|
||||
/*
|
||||
add an empty element to a message
|
||||
*/
|
||||
int ldb_msg_add_empty( struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
int flags,
|
||||
struct ldb_message_element **return_el)
|
||||
{
|
||||
struct ldb_message_element *els;
|
||||
|
||||
/* FIXME: we should probably leave this to the schema module to check */
|
||||
if (! ldb_valid_attr_name(attr_name)) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
els = talloc_realloc(msg, msg->elements,
|
||||
struct ldb_message_element, msg->num_elements+1);
|
||||
if (!els) {
|
||||
errno = ENOMEM;
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
els[msg->num_elements].values = NULL;
|
||||
els[msg->num_elements].num_values = 0;
|
||||
els[msg->num_elements].flags = flags;
|
||||
els[msg->num_elements].name = talloc_strdup(els, attr_name);
|
||||
if (!els[msg->num_elements].name) {
|
||||
errno = ENOMEM;
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
msg->elements = els;
|
||||
msg->num_elements++;
|
||||
|
||||
if (return_el) {
|
||||
*return_el = &els[msg->num_elements-1];
|
||||
}
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
add an empty element to a message
|
||||
*/
|
||||
int ldb_msg_add(struct ldb_message *msg,
|
||||
const struct ldb_message_element *el,
|
||||
int flags)
|
||||
{
|
||||
if (ldb_msg_add_empty(msg, el->name, flags, NULL) != 0) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
msg->elements[msg->num_elements-1] = *el;
|
||||
msg->elements[msg->num_elements-1].flags = flags;
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
add a value to a message
|
||||
*/
|
||||
int ldb_msg_add_value(struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
const struct ldb_val *val,
|
||||
struct ldb_message_element **return_el)
|
||||
{
|
||||
struct ldb_message_element *el;
|
||||
struct ldb_val *vals;
|
||||
int ret;
|
||||
|
||||
el = ldb_msg_find_element(msg, attr_name);
|
||||
if (!el) {
|
||||
ret = ldb_msg_add_empty(msg, attr_name, 0, &el);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
vals = talloc_realloc(msg, el->values, struct ldb_val, el->num_values+1);
|
||||
if (!vals) {
|
||||
errno = ENOMEM;
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
el->values = vals;
|
||||
el->values[el->num_values] = *val;
|
||||
el->num_values++;
|
||||
|
||||
if (return_el) {
|
||||
*return_el = el;
|
||||
}
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
add a value to a message, stealing it into the 'right' place
|
||||
*/
|
||||
int ldb_msg_add_steal_value(struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
struct ldb_val *val)
|
||||
{
|
||||
int ret;
|
||||
struct ldb_message_element *el;
|
||||
|
||||
ret = ldb_msg_add_value(msg, attr_name, val, &el);
|
||||
if (ret == LDB_SUCCESS) {
|
||||
talloc_steal(el->values, val->data);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
add a string element to a message
|
||||
*/
|
||||
int ldb_msg_add_string(struct ldb_message *msg,
|
||||
const char *attr_name, const char *str)
|
||||
{
|
||||
struct ldb_val val;
|
||||
|
||||
val.data = discard_const_p(uint8_t, str);
|
||||
val.length = strlen(str);
|
||||
|
||||
if (val.length == 0) {
|
||||
/* allow empty strings as non-existant attributes */
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
return ldb_msg_add_value(msg, attr_name, &val, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
add a string element to a message, stealing it into the 'right' place
|
||||
*/
|
||||
int ldb_msg_add_steal_string(struct ldb_message *msg,
|
||||
const char *attr_name, char *str)
|
||||
{
|
||||
struct ldb_val val;
|
||||
|
||||
val.data = (uint8_t *)str;
|
||||
val.length = strlen(str);
|
||||
|
||||
return ldb_msg_add_steal_value(msg, attr_name, &val);
|
||||
}
|
||||
|
||||
/*
|
||||
add a printf formatted element to a message
|
||||
*/
|
||||
int ldb_msg_add_fmt(struct ldb_message *msg,
|
||||
const char *attr_name, const char *fmt, ...)
|
||||
{
|
||||
struct ldb_val val;
|
||||
va_list ap;
|
||||
char *str;
|
||||
|
||||
va_start(ap, fmt);
|
||||
str = talloc_vasprintf(msg, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (str == NULL) return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
val.data = (uint8_t *)str;
|
||||
val.length = strlen(str);
|
||||
|
||||
return ldb_msg_add_steal_value(msg, attr_name, &val);
|
||||
}
|
||||
|
||||
/*
|
||||
compare two ldb_message_element structures
|
||||
assumes case senistive comparison
|
||||
*/
|
||||
int ldb_msg_element_compare(struct ldb_message_element *el1,
|
||||
struct ldb_message_element *el2)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (el1->num_values != el2->num_values) {
|
||||
return el1->num_values - el2->num_values;
|
||||
}
|
||||
|
||||
for (i=0;i<el1->num_values;i++) {
|
||||
if (!ldb_msg_find_val(el2, &el1->values[i])) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
compare two ldb_message_element structures
|
||||
comparing by element name
|
||||
*/
|
||||
int ldb_msg_element_compare_name(struct ldb_message_element *el1,
|
||||
struct ldb_message_element *el2)
|
||||
{
|
||||
return ldb_attr_cmp(el1->name, el2->name);
|
||||
}
|
||||
|
||||
/*
|
||||
convenience functions to return common types from a message
|
||||
these return the first value if the attribute is multi-valued
|
||||
*/
|
||||
const struct ldb_val *ldb_msg_find_ldb_val(const struct ldb_message *msg, const char *attr_name)
|
||||
{
|
||||
struct ldb_message_element *el = ldb_msg_find_element(msg, attr_name);
|
||||
if (!el || el->num_values == 0) {
|
||||
return NULL;
|
||||
}
|
||||
return &el->values[0];
|
||||
}
|
||||
|
||||
int ldb_msg_find_attr_as_int(const struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
int default_value)
|
||||
{
|
||||
const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
|
||||
if (!v || !v->data) {
|
||||
return default_value;
|
||||
}
|
||||
return strtol((const char *)v->data, NULL, 0);
|
||||
}
|
||||
|
||||
unsigned int ldb_msg_find_attr_as_uint(const struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
unsigned int default_value)
|
||||
{
|
||||
const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
|
||||
if (!v || !v->data) {
|
||||
return default_value;
|
||||
}
|
||||
return strtoul((const char *)v->data, NULL, 0);
|
||||
}
|
||||
|
||||
int64_t ldb_msg_find_attr_as_int64(const struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
int64_t default_value)
|
||||
{
|
||||
const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
|
||||
if (!v || !v->data) {
|
||||
return default_value;
|
||||
}
|
||||
return strtoll((const char *)v->data, NULL, 0);
|
||||
}
|
||||
|
||||
uint64_t ldb_msg_find_attr_as_uint64(const struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
uint64_t default_value)
|
||||
{
|
||||
const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
|
||||
if (!v || !v->data) {
|
||||
return default_value;
|
||||
}
|
||||
return strtoull((const char *)v->data, NULL, 0);
|
||||
}
|
||||
|
||||
double ldb_msg_find_attr_as_double(const struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
double default_value)
|
||||
{
|
||||
const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
|
||||
if (!v || !v->data) {
|
||||
return default_value;
|
||||
}
|
||||
return strtod((const char *)v->data, NULL);
|
||||
}
|
||||
|
||||
int ldb_msg_find_attr_as_bool(const struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
int default_value)
|
||||
{
|
||||
const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
|
||||
if (!v || !v->data) {
|
||||
return default_value;
|
||||
}
|
||||
if (strcasecmp((const char *)v->data, "FALSE") == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (strcasecmp((const char *)v->data, "TRUE") == 0) {
|
||||
return 1;
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
|
||||
const char *ldb_msg_find_attr_as_string(const struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
const char *default_value)
|
||||
{
|
||||
const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
|
||||
if (!v || !v->data) {
|
||||
return default_value;
|
||||
}
|
||||
return (const char *)v->data;
|
||||
}
|
||||
|
||||
struct ldb_dn *ldb_msg_find_attr_as_dn(struct ldb_context *ldb,
|
||||
void *mem_ctx,
|
||||
const struct ldb_message *msg,
|
||||
const char *attr_name)
|
||||
{
|
||||
struct ldb_dn *res_dn;
|
||||
const struct ldb_val *v;
|
||||
|
||||
v = ldb_msg_find_ldb_val(msg, attr_name);
|
||||
if (!v || !v->data) {
|
||||
return NULL;
|
||||
}
|
||||
res_dn = ldb_dn_new(mem_ctx, ldb, (const char *)v->data);
|
||||
if ( ! ldb_dn_validate(res_dn)) {
|
||||
talloc_free(res_dn);
|
||||
return NULL;
|
||||
}
|
||||
return res_dn;
|
||||
}
|
||||
|
||||
/*
|
||||
sort the elements of a message by name
|
||||
*/
|
||||
void ldb_msg_sort_elements(struct ldb_message *msg)
|
||||
{
|
||||
qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element),
|
||||
(comparison_fn_t)ldb_msg_element_compare_name);
|
||||
}
|
||||
|
||||
/*
|
||||
shallow copy a message - copying only the elements array so that the caller
|
||||
can safely add new elements without changing the message
|
||||
*/
|
||||
struct ldb_message *ldb_msg_copy_shallow(TALLOC_CTX *mem_ctx,
|
||||
const struct ldb_message *msg)
|
||||
{
|
||||
struct ldb_message *msg2;
|
||||
int i;
|
||||
|
||||
msg2 = talloc(mem_ctx, struct ldb_message);
|
||||
if (msg2 == NULL) return NULL;
|
||||
|
||||
*msg2 = *msg;
|
||||
msg2->private_data = NULL;
|
||||
|
||||
msg2->elements = talloc_array(msg2, struct ldb_message_element,
|
||||
msg2->num_elements);
|
||||
if (msg2->elements == NULL) goto failed;
|
||||
|
||||
for (i=0;i<msg2->num_elements;i++) {
|
||||
msg2->elements[i] = msg->elements[i];
|
||||
}
|
||||
|
||||
return msg2;
|
||||
|
||||
failed:
|
||||
talloc_free(msg2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
copy a message, allocating new memory for all parts
|
||||
*/
|
||||
struct ldb_message *ldb_msg_copy(TALLOC_CTX *mem_ctx,
|
||||
const struct ldb_message *msg)
|
||||
{
|
||||
struct ldb_message *msg2;
|
||||
int i, j;
|
||||
|
||||
msg2 = ldb_msg_copy_shallow(mem_ctx, msg);
|
||||
if (msg2 == NULL) return NULL;
|
||||
|
||||
msg2->dn = ldb_dn_copy(msg2, msg2->dn);
|
||||
if (msg2->dn == NULL) goto failed;
|
||||
|
||||
for (i=0;i<msg2->num_elements;i++) {
|
||||
struct ldb_message_element *el = &msg2->elements[i];
|
||||
struct ldb_val *values = el->values;
|
||||
el->name = talloc_strdup(msg2->elements, el->name);
|
||||
if (el->name == NULL) goto failed;
|
||||
el->values = talloc_array(msg2->elements, struct ldb_val, el->num_values);
|
||||
for (j=0;j<el->num_values;j++) {
|
||||
el->values[j] = ldb_val_dup(el->values, &values[j]);
|
||||
if (el->values[j].data == NULL && values[j].length != 0) {
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return msg2;
|
||||
|
||||
failed:
|
||||
talloc_free(msg2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
canonicalise a message, merging elements of the same name
|
||||
*/
|
||||
struct ldb_message *ldb_msg_canonicalize(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg)
|
||||
{
|
||||
int i;
|
||||
struct ldb_message *msg2;
|
||||
|
||||
msg2 = ldb_msg_copy(ldb, msg);
|
||||
if (msg2 == NULL) return NULL;
|
||||
|
||||
ldb_msg_sort_elements(msg2);
|
||||
|
||||
for (i=1;i<msg2->num_elements;i++) {
|
||||
struct ldb_message_element *el1 = &msg2->elements[i-1];
|
||||
struct ldb_message_element *el2 = &msg2->elements[i];
|
||||
if (ldb_msg_element_compare_name(el1, el2) == 0) {
|
||||
el1->values = talloc_realloc(msg2->elements, el1->values, struct ldb_val,
|
||||
el1->num_values + el2->num_values);
|
||||
if (el1->values == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(el1->values + el1->num_values,
|
||||
el2->values,
|
||||
sizeof(struct ldb_val) * el2->num_values);
|
||||
el1->num_values += el2->num_values;
|
||||
talloc_free(discard_const_p(char, el2->name));
|
||||
if (i+1<msg2->num_elements) {
|
||||
memmove(el2, el2+1, sizeof(struct ldb_message_element) *
|
||||
(msg2->num_elements - (i+1)));
|
||||
}
|
||||
msg2->num_elements--;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
return msg2;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
return a ldb_message representing the differences between msg1 and msg2. If you
|
||||
then use this in a ldb_modify() call it can be used to save edits to a message
|
||||
*/
|
||||
struct ldb_message *ldb_msg_diff(struct ldb_context *ldb,
|
||||
struct ldb_message *msg1,
|
||||
struct ldb_message *msg2)
|
||||
{
|
||||
struct ldb_message *mod;
|
||||
struct ldb_message_element *el;
|
||||
unsigned int i;
|
||||
|
||||
mod = ldb_msg_new(ldb);
|
||||
|
||||
mod->dn = msg1->dn;
|
||||
mod->num_elements = 0;
|
||||
mod->elements = NULL;
|
||||
|
||||
msg2 = ldb_msg_canonicalize(ldb, msg2);
|
||||
if (msg2 == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* look in msg2 to find elements that need to be added
|
||||
or modified */
|
||||
for (i=0;i<msg2->num_elements;i++) {
|
||||
el = ldb_msg_find_element(msg1, msg2->elements[i].name);
|
||||
|
||||
if (el && ldb_msg_element_compare(el, &msg2->elements[i]) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ldb_msg_add(mod,
|
||||
&msg2->elements[i],
|
||||
el?LDB_FLAG_MOD_REPLACE:LDB_FLAG_MOD_ADD) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* look in msg1 to find elements that need to be deleted */
|
||||
for (i=0;i<msg1->num_elements;i++) {
|
||||
el = ldb_msg_find_element(msg2, msg1->elements[i].name);
|
||||
if (!el) {
|
||||
if (ldb_msg_add_empty(mod,
|
||||
msg1->elements[i].name,
|
||||
LDB_FLAG_MOD_DELETE, NULL) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
int ldb_msg_sanity_check(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* basic check on DN */
|
||||
if (msg->dn == NULL) {
|
||||
/* TODO: return also an error string */
|
||||
ldb_set_errstring(ldb, "ldb message lacks a DN!");
|
||||
return LDB_ERR_INVALID_DN_SYNTAX;
|
||||
}
|
||||
|
||||
/* basic syntax checks */
|
||||
for (i = 0; i < msg->num_elements; i++) {
|
||||
for (j = 0; j < msg->elements[i].num_values; j++) {
|
||||
if (msg->elements[i].values[j].length == 0) {
|
||||
TALLOC_CTX *mem_ctx = talloc_new(ldb);
|
||||
/* an attribute cannot be empty */
|
||||
/* TODO: return also an error string */
|
||||
ldb_asprintf_errstring(ldb, "Element %s has empty attribute in ldb message (%s)!",
|
||||
msg->elements[i].name,
|
||||
ldb_dn_get_linearized(msg->dn));
|
||||
talloc_free(mem_ctx);
|
||||
return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
copy an attribute list. This only copies the array, not the elements
|
||||
(ie. the elements are left as the same pointers)
|
||||
*/
|
||||
const char **ldb_attr_list_copy(TALLOC_CTX *mem_ctx, const char * const *attrs)
|
||||
{
|
||||
const char **ret;
|
||||
int i;
|
||||
for (i=0;attrs[i];i++) /* noop */ ;
|
||||
ret = talloc_array(mem_ctx, const char *, i+1);
|
||||
if (ret == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
for (i=0;attrs[i];i++) {
|
||||
ret[i] = attrs[i];
|
||||
}
|
||||
ret[i] = attrs[i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
copy an attribute list. This only copies the array, not the elements
|
||||
(ie. the elements are left as the same pointers). The new attribute is added to the list.
|
||||
*/
|
||||
const char **ldb_attr_list_copy_add(TALLOC_CTX *mem_ctx, const char * const *attrs, const char *new_attr)
|
||||
{
|
||||
const char **ret;
|
||||
int i;
|
||||
for (i=0;attrs[i];i++) /* noop */ ;
|
||||
ret = talloc_array(mem_ctx, const char *, i+2);
|
||||
if (ret == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
for (i=0;attrs[i];i++) {
|
||||
ret[i] = attrs[i];
|
||||
}
|
||||
ret[i] = new_attr;
|
||||
ret[i+1] = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
return 1 if an attribute is in a list of attributes, or 0 otherwise
|
||||
*/
|
||||
int ldb_attr_in_list(const char * const *attrs, const char *attr)
|
||||
{
|
||||
int i;
|
||||
for (i=0;attrs[i];i++) {
|
||||
if (ldb_attr_cmp(attrs[i], attr) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
rename the specified attribute in a search result
|
||||
*/
|
||||
int ldb_msg_rename_attr(struct ldb_message *msg, const char *attr, const char *replace)
|
||||
{
|
||||
struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
|
||||
if (el == NULL) {
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
el->name = talloc_strdup(msg->elements, replace);
|
||||
if (el->name == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
copy the specified attribute in a search result to a new attribute
|
||||
*/
|
||||
int ldb_msg_copy_attr(struct ldb_message *msg, const char *attr, const char *replace)
|
||||
{
|
||||
struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
|
||||
if (el == NULL) {
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
if (ldb_msg_add(msg, el, 0) != 0) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
return ldb_msg_rename_attr(msg, attr, replace);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
remove the specified attribute in a search result
|
||||
*/
|
||||
void ldb_msg_remove_attr(struct ldb_message *msg, const char *attr)
|
||||
{
|
||||
struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
|
||||
if (el) {
|
||||
int n = (el - msg->elements);
|
||||
if (n != msg->num_elements-1) {
|
||||
memmove(el, el+1, ((msg->num_elements-1) - n)*sizeof(*el));
|
||||
}
|
||||
msg->num_elements--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
remove the specified element in a search result
|
||||
*/
|
||||
void ldb_msg_remove_element(struct ldb_message *msg, struct ldb_message_element *el)
|
||||
{
|
||||
int n = (el - msg->elements);
|
||||
if (n != msg->num_elements-1) {
|
||||
memmove(el, el+1, ((msg->num_elements-1) - n)*sizeof(*el));
|
||||
}
|
||||
msg->num_elements--;
|
||||
}
|
||||
|
||||
/*
|
||||
return a LDAP formatted time string
|
||||
*/
|
||||
char *ldb_timestring(TALLOC_CTX *mem_ctx, time_t t)
|
||||
{
|
||||
struct tm *tm = gmtime(&t);
|
||||
char *ts;
|
||||
int r;
|
||||
|
||||
if (!tm) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* we now excatly how long this string will be */
|
||||
ts = talloc_array(mem_ctx, char, 18);
|
||||
|
||||
/* formatted like: 20040408072012.0Z */
|
||||
r = snprintf(ts, 18,
|
||||
"%04u%02u%02u%02u%02u%02u.0Z",
|
||||
tm->tm_year+1900, tm->tm_mon+1,
|
||||
tm->tm_mday, tm->tm_hour, tm->tm_min,
|
||||
tm->tm_sec);
|
||||
|
||||
if (r != 17) {
|
||||
talloc_free(ts);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
convert a LDAP time string to a time_t. Return 0 if unable to convert
|
||||
*/
|
||||
time_t ldb_string_to_time(const char *s)
|
||||
{
|
||||
struct tm tm;
|
||||
|
||||
if (s == NULL) return 0;
|
||||
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
if (sscanf(s, "%04u%02u%02u%02u%02u%02u",
|
||||
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
|
||||
&tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
|
||||
return 0;
|
||||
}
|
||||
tm.tm_year -= 1900;
|
||||
tm.tm_mon -= 1;
|
||||
|
||||
return timegm(&tm);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
dump a set of results to a file. Useful from within gdb
|
||||
*/
|
||||
void ldb_dump_results(struct ldb_context *ldb, struct ldb_result *result, FILE *f)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < result->count; i++) {
|
||||
struct ldb_ldif ldif;
|
||||
fprintf(f, "# record %d\n", i+1);
|
||||
ldif.changetype = LDB_CHANGETYPE_NONE;
|
||||
ldif.msg = result->msgs[i];
|
||||
ldb_ldif_write_file(ldb, f, &ldif);
|
||||
}
|
||||
}
|
||||
|
||||
int ldb_msg_check_string_attribute(const struct ldb_message *msg, const char *name, const char *value)
|
||||
{
|
||||
struct ldb_message_element *el;
|
||||
struct ldb_val val;
|
||||
|
||||
el = ldb_msg_find_element(msg, name);
|
||||
if (el == NULL)
|
||||
return 0;
|
||||
|
||||
val.data = discard_const_p(uint8_t, value);
|
||||
val.length = strlen(value);
|
||||
|
||||
if (ldb_msg_find_val(el, &val))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,817 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb expression parsing
|
||||
*
|
||||
* Description: parse LDAP-like search expressions
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*/
|
||||
|
||||
/*
|
||||
TODO:
|
||||
- add RFC2254 binary string handling
|
||||
- possibly add ~=, <= and >= handling
|
||||
- expand the test suite
|
||||
- add better parse error handling
|
||||
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
#include "system/locale.h"
|
||||
|
||||
/*
|
||||
a filter is defined by:
|
||||
<filter> ::= '(' <filtercomp> ')'
|
||||
<filtercomp> ::= <and> | <or> | <not> | <simple>
|
||||
<and> ::= '&' <filterlist>
|
||||
<or> ::= '|' <filterlist>
|
||||
<not> ::= '!' <filter>
|
||||
<filterlist> ::= <filter> | <filter> <filterlist>
|
||||
<simple> ::= <attributetype> <filtertype> <attributevalue>
|
||||
<filtertype> ::= '=' | '~=' | '<=' | '>='
|
||||
*/
|
||||
|
||||
/*
|
||||
decode a RFC2254 binary string representation of a buffer.
|
||||
Used in LDAP filters.
|
||||
*/
|
||||
struct ldb_val ldb_binary_decode(void *mem_ctx, const char *str)
|
||||
{
|
||||
int i, j;
|
||||
struct ldb_val ret;
|
||||
int slen = str?strlen(str):0;
|
||||
|
||||
ret.data = (uint8_t *)talloc_size(mem_ctx, slen+1);
|
||||
ret.length = 0;
|
||||
if (ret.data == NULL) return ret;
|
||||
|
||||
for (i=j=0;i<slen;i++) {
|
||||
if (str[i] == '\\') {
|
||||
unsigned c;
|
||||
if (sscanf(&str[i+1], "%02X", &c) != 1) {
|
||||
talloc_free(ret.data);
|
||||
memset(&ret, 0, sizeof(ret));
|
||||
return ret;
|
||||
}
|
||||
((uint8_t *)ret.data)[j++] = c;
|
||||
i += 2;
|
||||
} else {
|
||||
((uint8_t *)ret.data)[j++] = str[i];
|
||||
}
|
||||
}
|
||||
ret.length = j;
|
||||
((uint8_t *)ret.data)[j] = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
encode a blob as a RFC2254 binary string, escaping any
|
||||
non-printable or '\' characters
|
||||
*/
|
||||
char *ldb_binary_encode(void *mem_ctx, struct ldb_val val)
|
||||
{
|
||||
int i;
|
||||
char *ret;
|
||||
int len = val.length;
|
||||
unsigned char *buf = val.data;
|
||||
|
||||
for (i=0;i<val.length;i++) {
|
||||
if (!isprint(buf[i]) || strchr(" *()\\&|!\"", buf[i])) {
|
||||
len += 2;
|
||||
}
|
||||
}
|
||||
ret = talloc_array(mem_ctx, char, len+1);
|
||||
if (ret == NULL) return NULL;
|
||||
|
||||
len = 0;
|
||||
for (i=0;i<val.length;i++) {
|
||||
if (!isprint(buf[i]) || strchr(" *()\\&|!\"", buf[i])) {
|
||||
snprintf(ret+len, 4, "\\%02X", buf[i]);
|
||||
len += 3;
|
||||
} else {
|
||||
ret[len++] = buf[i];
|
||||
}
|
||||
}
|
||||
|
||||
ret[len] = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
encode a string as a RFC2254 binary string, escaping any
|
||||
non-printable or '\' characters. This routine is suitable for use
|
||||
in escaping user data in ldap filters.
|
||||
*/
|
||||
char *ldb_binary_encode_string(void *mem_ctx, const char *string)
|
||||
{
|
||||
struct ldb_val val;
|
||||
val.data = discard_const_p(uint8_t, string);
|
||||
val.length = strlen(string);
|
||||
return ldb_binary_encode(mem_ctx, val);
|
||||
}
|
||||
|
||||
/* find the first matching wildcard */
|
||||
static char *ldb_parse_find_wildcard(char *value)
|
||||
{
|
||||
while (*value) {
|
||||
value = strpbrk(value, "\\*");
|
||||
if (value == NULL) return NULL;
|
||||
|
||||
if (value[0] == '\\') {
|
||||
if (value[1] == '\0') return NULL;
|
||||
value += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value[0] == '*') return value;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* return a NULL terminated list of binary strings representing the value
|
||||
chunks separated by wildcards that makes the value portion of the filter
|
||||
*/
|
||||
static struct ldb_val **ldb_wildcard_decode(void *mem_ctx, const char *string)
|
||||
{
|
||||
struct ldb_val **ret = NULL;
|
||||
int val = 0;
|
||||
char *wc, *str;
|
||||
|
||||
wc = talloc_strdup(mem_ctx, string);
|
||||
if (wc == NULL) return NULL;
|
||||
|
||||
while (wc && *wc) {
|
||||
str = wc;
|
||||
wc = ldb_parse_find_wildcard(str);
|
||||
if (wc && *wc) {
|
||||
if (wc == str) {
|
||||
wc++;
|
||||
continue;
|
||||
}
|
||||
*wc = 0;
|
||||
wc++;
|
||||
}
|
||||
|
||||
ret = talloc_realloc(mem_ctx, ret, struct ldb_val *, val + 2);
|
||||
if (ret == NULL) return NULL;
|
||||
|
||||
ret[val] = talloc(mem_ctx, struct ldb_val);
|
||||
if (ret[val] == NULL) return NULL;
|
||||
|
||||
*(ret[val]) = ldb_binary_decode(mem_ctx, str);
|
||||
if ((ret[val])->data == NULL) return NULL;
|
||||
|
||||
val++;
|
||||
}
|
||||
|
||||
if (ret != NULL) {
|
||||
ret[val] = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct ldb_parse_tree *ldb_parse_filter(void *mem_ctx, const char **s);
|
||||
|
||||
|
||||
/*
|
||||
parse an extended match
|
||||
|
||||
possible forms:
|
||||
(attr:oid:=value)
|
||||
(attr:dn:oid:=value)
|
||||
(attr:dn:=value)
|
||||
(:dn:oid:=value)
|
||||
|
||||
the ':dn' part sets the dnAttributes boolean if present
|
||||
the oid sets the rule_id string
|
||||
|
||||
*/
|
||||
static struct ldb_parse_tree *ldb_parse_extended(struct ldb_parse_tree *ret,
|
||||
char *attr, char *value)
|
||||
{
|
||||
char *p1, *p2;
|
||||
|
||||
ret->operation = LDB_OP_EXTENDED;
|
||||
ret->u.extended.value = ldb_binary_decode(ret, value);
|
||||
if (ret->u.extended.value.data == NULL) goto failed;
|
||||
|
||||
p1 = strchr(attr, ':');
|
||||
if (p1 == NULL) goto failed;
|
||||
p2 = strchr(p1+1, ':');
|
||||
|
||||
*p1 = 0;
|
||||
if (p2) *p2 = 0;
|
||||
|
||||
ret->u.extended.attr = attr;
|
||||
if (strcmp(p1+1, "dn") == 0) {
|
||||
ret->u.extended.dnAttributes = 1;
|
||||
if (p2) {
|
||||
ret->u.extended.rule_id = talloc_strdup(ret, p2+1);
|
||||
if (ret->u.extended.rule_id == NULL) goto failed;
|
||||
} else {
|
||||
ret->u.extended.rule_id = NULL;
|
||||
}
|
||||
} else {
|
||||
ret->u.extended.dnAttributes = 0;
|
||||
ret->u.extended.rule_id = talloc_strdup(ret, p1+1);
|
||||
if (ret->u.extended.rule_id == NULL) goto failed;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
failed:
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static enum ldb_parse_op ldb_parse_filtertype(void *mem_ctx, char **type, char **value, const char **s)
|
||||
{
|
||||
enum ldb_parse_op filter = 0;
|
||||
char *name, *val, *k;
|
||||
const char *p = *s;
|
||||
const char *t, *t1;
|
||||
|
||||
/* retrieve attributetype name */
|
||||
t = p;
|
||||
|
||||
while ((isascii(*p) && isalnum((unsigned char)*p)) || (*p == '-')) { /* attribute names can only be alphanums */
|
||||
p++;
|
||||
}
|
||||
|
||||
if (*p == ':') { /* but extended searches have : and . chars too */
|
||||
p = strstr(p, ":=");
|
||||
if (p == NULL) { /* malformed attribute name */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
t1 = p;
|
||||
|
||||
while (isspace((unsigned char)*p)) p++;
|
||||
|
||||
if (!strchr("=<>~:", *p)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* save name */
|
||||
name = (char *)talloc_memdup(mem_ctx, t, t1 - t + 1);
|
||||
if (name == NULL) return 0;
|
||||
name[t1 - t] = '\0';
|
||||
|
||||
/* retrieve filtertype */
|
||||
|
||||
if (*p == '=') {
|
||||
filter = LDB_OP_EQUALITY;
|
||||
} else if (*(p + 1) == '=') {
|
||||
switch (*p) {
|
||||
case '<':
|
||||
filter = LDB_OP_LESS;
|
||||
p++;
|
||||
break;
|
||||
case '>':
|
||||
filter = LDB_OP_GREATER;
|
||||
p++;
|
||||
break;
|
||||
case '~':
|
||||
filter = LDB_OP_APPROX;
|
||||
p++;
|
||||
break;
|
||||
case ':':
|
||||
filter = LDB_OP_EXTENDED;
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!filter) {
|
||||
talloc_free(name);
|
||||
return filter;
|
||||
}
|
||||
p++;
|
||||
|
||||
while (isspace((unsigned char)*p)) p++;
|
||||
|
||||
/* retieve value */
|
||||
t = p;
|
||||
|
||||
while (*p && ((*p != ')') || ((*p == ')') && (*(p - 1) == '\\')))) p++;
|
||||
|
||||
val = (char *)talloc_memdup(mem_ctx, t, p - t + 1);
|
||||
if (val == NULL) {
|
||||
talloc_free(name);
|
||||
return 0;
|
||||
}
|
||||
val[p - t] = '\0';
|
||||
|
||||
k = &(val[p - t]);
|
||||
|
||||
/* remove trailing spaces from value */
|
||||
while ((k > val) && (isspace((unsigned char)*(k - 1)))) k--;
|
||||
*k = '\0';
|
||||
|
||||
*type = name;
|
||||
*value = val;
|
||||
*s = p;
|
||||
return filter;
|
||||
}
|
||||
|
||||
/*
|
||||
<simple> ::= <attributetype> <filtertype> <attributevalue>
|
||||
*/
|
||||
static struct ldb_parse_tree *ldb_parse_simple(void *mem_ctx, const char **s)
|
||||
{
|
||||
char *attr, *value;
|
||||
struct ldb_parse_tree *ret;
|
||||
enum ldb_parse_op filtertype;
|
||||
|
||||
ret = talloc(mem_ctx, struct ldb_parse_tree);
|
||||
if (!ret) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
filtertype = ldb_parse_filtertype(ret, &attr, &value, s);
|
||||
if (!filtertype) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (filtertype) {
|
||||
|
||||
case LDB_OP_PRESENT:
|
||||
ret->operation = LDB_OP_PRESENT;
|
||||
ret->u.present.attr = attr;
|
||||
break;
|
||||
|
||||
case LDB_OP_EQUALITY:
|
||||
|
||||
if (strcmp(value, "*") == 0) {
|
||||
ret->operation = LDB_OP_PRESENT;
|
||||
ret->u.present.attr = attr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ldb_parse_find_wildcard(value) != NULL) {
|
||||
ret->operation = LDB_OP_SUBSTRING;
|
||||
ret->u.substring.attr = attr;
|
||||
ret->u.substring.start_with_wildcard = 0;
|
||||
ret->u.substring.end_with_wildcard = 0;
|
||||
ret->u.substring.chunks = ldb_wildcard_decode(ret, value);
|
||||
if (ret->u.substring.chunks == NULL){
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
if (value[0] == '*')
|
||||
ret->u.substring.start_with_wildcard = 1;
|
||||
if (value[strlen(value) - 1] == '*')
|
||||
ret->u.substring.end_with_wildcard = 1;
|
||||
talloc_free(value);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ret->operation = LDB_OP_EQUALITY;
|
||||
ret->u.equality.attr = attr;
|
||||
ret->u.equality.value = ldb_binary_decode(ret, value);
|
||||
if (ret->u.equality.value.data == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
talloc_free(value);
|
||||
break;
|
||||
|
||||
case LDB_OP_GREATER:
|
||||
ret->operation = LDB_OP_GREATER;
|
||||
ret->u.comparison.attr = attr;
|
||||
ret->u.comparison.value = ldb_binary_decode(ret, value);
|
||||
if (ret->u.comparison.value.data == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
talloc_free(value);
|
||||
break;
|
||||
|
||||
case LDB_OP_LESS:
|
||||
ret->operation = LDB_OP_LESS;
|
||||
ret->u.comparison.attr = attr;
|
||||
ret->u.comparison.value = ldb_binary_decode(ret, value);
|
||||
if (ret->u.comparison.value.data == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
talloc_free(value);
|
||||
break;
|
||||
|
||||
case LDB_OP_APPROX:
|
||||
ret->operation = LDB_OP_APPROX;
|
||||
ret->u.comparison.attr = attr;
|
||||
ret->u.comparison.value = ldb_binary_decode(ret, value);
|
||||
if (ret->u.comparison.value.data == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
talloc_free(value);
|
||||
break;
|
||||
|
||||
case LDB_OP_EXTENDED:
|
||||
|
||||
ret = ldb_parse_extended(ret, attr, value);
|
||||
break;
|
||||
|
||||
default:
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
parse a filterlist
|
||||
<and> ::= '&' <filterlist>
|
||||
<or> ::= '|' <filterlist>
|
||||
<filterlist> ::= <filter> | <filter> <filterlist>
|
||||
*/
|
||||
static struct ldb_parse_tree *ldb_parse_filterlist(void *mem_ctx, const char **s)
|
||||
{
|
||||
struct ldb_parse_tree *ret, *next;
|
||||
enum ldb_parse_op op;
|
||||
const char *p = *s;
|
||||
|
||||
switch (*p) {
|
||||
case '&':
|
||||
op = LDB_OP_AND;
|
||||
break;
|
||||
case '|':
|
||||
op = LDB_OP_OR;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
p++;
|
||||
|
||||
while (isspace((unsigned char)*p)) p++;
|
||||
|
||||
ret = talloc(mem_ctx, struct ldb_parse_tree);
|
||||
if (!ret) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->operation = op;
|
||||
ret->u.list.num_elements = 1;
|
||||
ret->u.list.elements = talloc(ret, struct ldb_parse_tree *);
|
||||
if (!ret->u.list.elements) {
|
||||
errno = ENOMEM;
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->u.list.elements[0] = ldb_parse_filter(ret->u.list.elements, &p);
|
||||
if (!ret->u.list.elements[0]) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (isspace((unsigned char)*p)) p++;
|
||||
|
||||
while (*p && (next = ldb_parse_filter(ret->u.list.elements, &p))) {
|
||||
struct ldb_parse_tree **e;
|
||||
e = talloc_realloc(ret, ret->u.list.elements,
|
||||
struct ldb_parse_tree *,
|
||||
ret->u.list.num_elements + 1);
|
||||
if (!e) {
|
||||
errno = ENOMEM;
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
ret->u.list.elements = e;
|
||||
ret->u.list.elements[ret->u.list.num_elements] = next;
|
||||
ret->u.list.num_elements++;
|
||||
while (isspace((unsigned char)*p)) p++;
|
||||
}
|
||||
|
||||
*s = p;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
<not> ::= '!' <filter>
|
||||
*/
|
||||
static struct ldb_parse_tree *ldb_parse_not(void *mem_ctx, const char **s)
|
||||
{
|
||||
struct ldb_parse_tree *ret;
|
||||
const char *p = *s;
|
||||
|
||||
if (*p != '!') {
|
||||
return NULL;
|
||||
}
|
||||
p++;
|
||||
|
||||
ret = talloc(mem_ctx, struct ldb_parse_tree);
|
||||
if (!ret) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->operation = LDB_OP_NOT;
|
||||
ret->u.isnot.child = ldb_parse_filter(ret, &p);
|
||||
if (!ret->u.isnot.child) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*s = p;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
parse a filtercomp
|
||||
<filtercomp> ::= <and> | <or> | <not> | <simple>
|
||||
*/
|
||||
static struct ldb_parse_tree *ldb_parse_filtercomp(void *mem_ctx, const char **s)
|
||||
{
|
||||
struct ldb_parse_tree *ret;
|
||||
const char *p = *s;
|
||||
|
||||
while (isspace((unsigned char)*p)) p++;
|
||||
|
||||
switch (*p) {
|
||||
case '&':
|
||||
ret = ldb_parse_filterlist(mem_ctx, &p);
|
||||
break;
|
||||
|
||||
case '|':
|
||||
ret = ldb_parse_filterlist(mem_ctx, &p);
|
||||
break;
|
||||
|
||||
case '!':
|
||||
ret = ldb_parse_not(mem_ctx, &p);
|
||||
break;
|
||||
|
||||
case '(':
|
||||
case ')':
|
||||
return NULL;
|
||||
|
||||
default:
|
||||
ret = ldb_parse_simple(mem_ctx, &p);
|
||||
|
||||
}
|
||||
|
||||
*s = p;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
<filter> ::= '(' <filtercomp> ')'
|
||||
*/
|
||||
static struct ldb_parse_tree *ldb_parse_filter(void *mem_ctx, const char **s)
|
||||
{
|
||||
struct ldb_parse_tree *ret;
|
||||
const char *p = *s;
|
||||
|
||||
if (*p != '(') {
|
||||
return NULL;
|
||||
}
|
||||
p++;
|
||||
|
||||
ret = ldb_parse_filtercomp(mem_ctx, &p);
|
||||
|
||||
if (*p != ')') {
|
||||
return NULL;
|
||||
}
|
||||
p++;
|
||||
|
||||
while (isspace((unsigned char)*p)) {
|
||||
p++;
|
||||
}
|
||||
|
||||
*s = p;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
main parser entry point. Takes a search string and returns a parse tree
|
||||
|
||||
expression ::= <simple> | <filter>
|
||||
*/
|
||||
struct ldb_parse_tree *ldb_parse_tree(void *mem_ctx, const char *s)
|
||||
{
|
||||
if (s == NULL || *s == 0) {
|
||||
s = "(|(objectClass=*)(distinguishedName=*))";
|
||||
}
|
||||
|
||||
while (isspace((unsigned char)*s)) s++;
|
||||
|
||||
if (*s == '(') {
|
||||
return ldb_parse_filter(mem_ctx, &s);
|
||||
}
|
||||
|
||||
return ldb_parse_simple(mem_ctx, &s);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
construct a ldap parse filter given a parse tree
|
||||
*/
|
||||
char *ldb_filter_from_tree(void *mem_ctx, struct ldb_parse_tree *tree)
|
||||
{
|
||||
char *s, *s2, *ret;
|
||||
int i;
|
||||
|
||||
if (tree == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (tree->operation) {
|
||||
case LDB_OP_AND:
|
||||
case LDB_OP_OR:
|
||||
ret = talloc_asprintf(mem_ctx, "(%c", tree->operation==LDB_OP_AND?'&':'|');
|
||||
if (ret == NULL) return NULL;
|
||||
for (i=0;i<tree->u.list.num_elements;i++) {
|
||||
s = ldb_filter_from_tree(mem_ctx, tree->u.list.elements[i]);
|
||||
if (s == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
s2 = talloc_asprintf_append(ret, "%s", s);
|
||||
talloc_free(s);
|
||||
if (s2 == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
ret = s2;
|
||||
}
|
||||
s = talloc_asprintf_append(ret, ")");
|
||||
if (s == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
return s;
|
||||
case LDB_OP_NOT:
|
||||
s = ldb_filter_from_tree(mem_ctx, tree->u.isnot.child);
|
||||
if (s == NULL) return NULL;
|
||||
|
||||
ret = talloc_asprintf(mem_ctx, "(!%s)", s);
|
||||
talloc_free(s);
|
||||
return ret;
|
||||
case LDB_OP_EQUALITY:
|
||||
s = ldb_binary_encode(mem_ctx, tree->u.equality.value);
|
||||
if (s == NULL) return NULL;
|
||||
ret = talloc_asprintf(mem_ctx, "(%s=%s)",
|
||||
tree->u.equality.attr, s);
|
||||
talloc_free(s);
|
||||
return ret;
|
||||
case LDB_OP_SUBSTRING:
|
||||
ret = talloc_asprintf(mem_ctx, "(%s=%s", tree->u.substring.attr,
|
||||
tree->u.substring.start_with_wildcard?"*":"");
|
||||
if (ret == NULL) return NULL;
|
||||
for (i = 0; tree->u.substring.chunks[i]; i++) {
|
||||
s2 = ldb_binary_encode(mem_ctx, *(tree->u.substring.chunks[i]));
|
||||
if (s2 == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
if (tree->u.substring.chunks[i+1] ||
|
||||
tree->u.substring.end_with_wildcard) {
|
||||
s = talloc_asprintf_append(ret, "%s*", s2);
|
||||
} else {
|
||||
s = talloc_asprintf_append(ret, "%s", s2);
|
||||
}
|
||||
if (s == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
ret = s;
|
||||
}
|
||||
s = talloc_asprintf_append(ret, ")");
|
||||
if (s == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
ret = s;
|
||||
return ret;
|
||||
case LDB_OP_GREATER:
|
||||
s = ldb_binary_encode(mem_ctx, tree->u.equality.value);
|
||||
if (s == NULL) return NULL;
|
||||
ret = talloc_asprintf(mem_ctx, "(%s>=%s)",
|
||||
tree->u.equality.attr, s);
|
||||
talloc_free(s);
|
||||
return ret;
|
||||
case LDB_OP_LESS:
|
||||
s = ldb_binary_encode(mem_ctx, tree->u.equality.value);
|
||||
if (s == NULL) return NULL;
|
||||
ret = talloc_asprintf(mem_ctx, "(%s<=%s)",
|
||||
tree->u.equality.attr, s);
|
||||
talloc_free(s);
|
||||
return ret;
|
||||
case LDB_OP_PRESENT:
|
||||
ret = talloc_asprintf(mem_ctx, "(%s=*)", tree->u.present.attr);
|
||||
return ret;
|
||||
case LDB_OP_APPROX:
|
||||
s = ldb_binary_encode(mem_ctx, tree->u.equality.value);
|
||||
if (s == NULL) return NULL;
|
||||
ret = talloc_asprintf(mem_ctx, "(%s~=%s)",
|
||||
tree->u.equality.attr, s);
|
||||
talloc_free(s);
|
||||
return ret;
|
||||
case LDB_OP_EXTENDED:
|
||||
s = ldb_binary_encode(mem_ctx, tree->u.extended.value);
|
||||
if (s == NULL) return NULL;
|
||||
ret = talloc_asprintf(mem_ctx, "(%s%s%s%s:=%s)",
|
||||
tree->u.extended.attr?tree->u.extended.attr:"",
|
||||
tree->u.extended.dnAttributes?":dn":"",
|
||||
tree->u.extended.rule_id?":":"",
|
||||
tree->u.extended.rule_id?tree->u.extended.rule_id:"",
|
||||
s);
|
||||
talloc_free(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
replace any occurances of an attribute name in the parse tree with a
|
||||
new name
|
||||
*/
|
||||
void ldb_parse_tree_attr_replace(struct ldb_parse_tree *tree,
|
||||
const char *attr,
|
||||
const char *replace)
|
||||
{
|
||||
int i;
|
||||
switch (tree->operation) {
|
||||
case LDB_OP_AND:
|
||||
case LDB_OP_OR:
|
||||
for (i=0;i<tree->u.list.num_elements;i++) {
|
||||
ldb_parse_tree_attr_replace(tree->u.list.elements[i],
|
||||
attr, replace);
|
||||
}
|
||||
break;
|
||||
case LDB_OP_NOT:
|
||||
ldb_parse_tree_attr_replace(tree->u.isnot.child, attr, replace);
|
||||
break;
|
||||
case LDB_OP_EQUALITY:
|
||||
case LDB_OP_GREATER:
|
||||
case LDB_OP_LESS:
|
||||
case LDB_OP_APPROX:
|
||||
if (ldb_attr_cmp(tree->u.equality.attr, attr) == 0) {
|
||||
tree->u.equality.attr = replace;
|
||||
}
|
||||
break;
|
||||
case LDB_OP_SUBSTRING:
|
||||
if (ldb_attr_cmp(tree->u.substring.attr, attr) == 0) {
|
||||
tree->u.substring.attr = replace;
|
||||
}
|
||||
break;
|
||||
case LDB_OP_PRESENT:
|
||||
if (ldb_attr_cmp(tree->u.present.attr, attr) == 0) {
|
||||
tree->u.present.attr = replace;
|
||||
}
|
||||
break;
|
||||
case LDB_OP_EXTENDED:
|
||||
if (tree->u.extended.attr &&
|
||||
ldb_attr_cmp(tree->u.extended.attr, attr) == 0) {
|
||||
tree->u.extended.attr = replace;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb utf8 handling
|
||||
*
|
||||
* Description: case folding and case comparison for UTF8 strings
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
#include "system/locale.h"
|
||||
|
||||
|
||||
/*
|
||||
this allow the user to pass in a caseless comparison
|
||||
function to handle utf8 caseless comparisons
|
||||
*/
|
||||
void ldb_set_utf8_fns(struct ldb_context *ldb,
|
||||
void *context,
|
||||
char *(*casefold)(void *, void *, const char *))
|
||||
{
|
||||
if (context)
|
||||
ldb->utf8_fns.context = context;
|
||||
if (casefold)
|
||||
ldb->utf8_fns.casefold = casefold;
|
||||
}
|
||||
|
||||
/*
|
||||
a simple case folding function
|
||||
NOTE: does not handle UTF8
|
||||
*/
|
||||
char *ldb_casefold_default(void *context, void *mem_ctx, const char *s)
|
||||
{
|
||||
int i;
|
||||
char *ret = talloc_strdup(mem_ctx, s);
|
||||
if (!s) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
for (i=0;ret[i];i++) {
|
||||
ret[i] = toupper((unsigned char)ret[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ldb_set_utf8_default(struct ldb_context *ldb)
|
||||
{
|
||||
ldb_set_utf8_fns(ldb, NULL, ldb_casefold_default);
|
||||
}
|
||||
|
||||
char *ldb_casefold(struct ldb_context *ldb, void *mem_ctx, const char *s)
|
||||
{
|
||||
return ldb->utf8_fns.casefold(ldb->utf8_fns.context, mem_ctx, s);
|
||||
}
|
||||
|
||||
/*
|
||||
check the attribute name is valid according to rfc2251
|
||||
returns 1 if the name is ok
|
||||
*/
|
||||
|
||||
int ldb_valid_attr_name(const char *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!s || !s[0])
|
||||
return 0;
|
||||
|
||||
/* handle special ldb_tdb wildcard */
|
||||
if (strcmp(s, "*") == 0) return 1;
|
||||
|
||||
for (i = 0; s[i]; i++) {
|
||||
if (! isascii(s[i])) {
|
||||
return 0;
|
||||
}
|
||||
if (i == 0) { /* first char must be an alpha (or our special '@' identifier) */
|
||||
if (! (isalpha(s[i]) || (s[i] == '@'))) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (! (isalnum(s[i]) || (s[i] == '-'))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *ldb_attr_casefold(void *mem_ctx, const char *s)
|
||||
{
|
||||
int i;
|
||||
char *ret = talloc_strdup(mem_ctx, s);
|
||||
if (!ret) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; ret[i]; i++) {
|
||||
ret[i] = toupper((unsigned char)ret[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
we accept either 'dn' or 'distinguishedName' for a distinguishedName
|
||||
*/
|
||||
int ldb_attr_dn(const char *attr)
|
||||
{
|
||||
if (ldb_attr_cmp(attr, "dn") == 0 ||
|
||||
ldb_attr_cmp(attr, "distinguishedName") == 0) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/* Copyright (C) 1991,1992,1996,1997,1999,2004 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
Written by Douglas C. Schmidt (schmidt@ics.uci.edu).
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA. */
|
||||
|
||||
/* If you consider tuning this algorithm, you should consult first:
|
||||
Engineering a sort function; Jon Bentley and M. Douglas McIlroy;
|
||||
Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993. */
|
||||
|
||||
/* Modified to be used in samba4 by
|
||||
* Simo Sorce <idra@samba.org> 2005
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
/* Byte-wise swap two items of size SIZE. */
|
||||
#define SWAP(a, b, size) \
|
||||
do \
|
||||
{ \
|
||||
register size_t __size = (size); \
|
||||
register char *__a = (a), *__b = (b); \
|
||||
do \
|
||||
{ \
|
||||
char __tmp = *__a; \
|
||||
*__a++ = *__b; \
|
||||
*__b++ = __tmp; \
|
||||
} while (--__size > 0); \
|
||||
} while (0)
|
||||
|
||||
/* Discontinue quicksort algorithm when partition gets below this size.
|
||||
This particular magic number was chosen to work best on a Sun 4/260. */
|
||||
#define MAX_THRESH 4
|
||||
|
||||
/* Stack node declarations used to store unfulfilled partition obligations. */
|
||||
typedef struct
|
||||
{
|
||||
char *lo;
|
||||
char *hi;
|
||||
} stack_node;
|
||||
|
||||
/* The next 4 #defines implement a very fast in-line stack abstraction. */
|
||||
/* The stack needs log (total_elements) entries (we could even subtract
|
||||
log(MAX_THRESH)). Since total_elements has type size_t, we get as
|
||||
upper bound for log (total_elements):
|
||||
bits per byte (CHAR_BIT) * sizeof(size_t). */
|
||||
#ifndef CHAR_BIT
|
||||
#define CHAR_BIT 8
|
||||
#endif
|
||||
#define STACK_SIZE (CHAR_BIT * sizeof(size_t))
|
||||
#define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top))
|
||||
#define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi)))
|
||||
#define STACK_NOT_EMPTY (stack < top)
|
||||
|
||||
|
||||
/* Order size using quicksort. This implementation incorporates
|
||||
four optimizations discussed in Sedgewick:
|
||||
|
||||
1. Non-recursive, using an explicit stack of pointer that store the
|
||||
next array partition to sort. To save time, this maximum amount
|
||||
of space required to store an array of SIZE_MAX is allocated on the
|
||||
stack. Assuming a 32-bit (64 bit) integer for size_t, this needs
|
||||
only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes).
|
||||
Pretty cheap, actually.
|
||||
|
||||
2. Chose the pivot element using a median-of-three decision tree.
|
||||
This reduces the probability of selecting a bad pivot value and
|
||||
eliminates certain extraneous comparisons.
|
||||
|
||||
3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving
|
||||
insertion sort to order the MAX_THRESH items within each partition.
|
||||
This is a big win, since insertion sort is faster for small, mostly
|
||||
sorted array segments.
|
||||
|
||||
4. The larger of the two sub-partitions is always pushed onto the
|
||||
stack first, with the algorithm then concentrating on the
|
||||
smaller partition. This *guarantees* no more than log (total_elems)
|
||||
stack size is needed (actually O(1) in this case)! */
|
||||
|
||||
void ldb_qsort (void *const pbase, size_t total_elems, size_t size,
|
||||
void *opaque, ldb_qsort_cmp_fn_t cmp)
|
||||
{
|
||||
register char *base_ptr = (char *) pbase;
|
||||
|
||||
const size_t max_thresh = MAX_THRESH * size;
|
||||
|
||||
if (total_elems == 0)
|
||||
/* Avoid lossage with unsigned arithmetic below. */
|
||||
return;
|
||||
|
||||
if (total_elems > MAX_THRESH)
|
||||
{
|
||||
char *lo = base_ptr;
|
||||
char *hi = &lo[size * (total_elems - 1)];
|
||||
stack_node stack[STACK_SIZE];
|
||||
stack_node *top = stack;
|
||||
|
||||
PUSH (NULL, NULL);
|
||||
|
||||
while (STACK_NOT_EMPTY)
|
||||
{
|
||||
char *left_ptr;
|
||||
char *right_ptr;
|
||||
|
||||
/* Select median value from among LO, MID, and HI. Rearrange
|
||||
LO and HI so the three values are sorted. This lowers the
|
||||
probability of picking a pathological pivot value and
|
||||
skips a comparison for both the LEFT_PTR and RIGHT_PTR in
|
||||
the while loops. */
|
||||
|
||||
char *mid = lo + size * ((hi - lo) / size >> 1);
|
||||
|
||||
if ((*cmp) ((void *) mid, (void *) lo, opaque) < 0)
|
||||
SWAP (mid, lo, size);
|
||||
if ((*cmp) ((void *) hi, (void *) mid, opaque) < 0)
|
||||
SWAP (mid, hi, size);
|
||||
else
|
||||
goto jump_over;
|
||||
if ((*cmp) ((void *) mid, (void *) lo, opaque) < 0)
|
||||
SWAP (mid, lo, size);
|
||||
jump_over:;
|
||||
|
||||
left_ptr = lo + size;
|
||||
right_ptr = hi - size;
|
||||
|
||||
/* Here's the famous ``collapse the walls'' section of quicksort.
|
||||
Gotta like those tight inner loops! They are the main reason
|
||||
that this algorithm runs much faster than others. */
|
||||
do
|
||||
{
|
||||
while ((*cmp) ((void *) left_ptr, (void *) mid, opaque) < 0)
|
||||
left_ptr += size;
|
||||
|
||||
while ((*cmp) ((void *) mid, (void *) right_ptr, opaque) < 0)
|
||||
right_ptr -= size;
|
||||
|
||||
if (left_ptr < right_ptr)
|
||||
{
|
||||
SWAP (left_ptr, right_ptr, size);
|
||||
if (mid == left_ptr)
|
||||
mid = right_ptr;
|
||||
else if (mid == right_ptr)
|
||||
mid = left_ptr;
|
||||
left_ptr += size;
|
||||
right_ptr -= size;
|
||||
}
|
||||
else if (left_ptr == right_ptr)
|
||||
{
|
||||
left_ptr += size;
|
||||
right_ptr -= size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (left_ptr <= right_ptr);
|
||||
|
||||
/* Set up pointers for next iteration. First determine whether
|
||||
left and right partitions are below the threshold size. If so,
|
||||
ignore one or both. Otherwise, push the larger partition's
|
||||
bounds on the stack and continue sorting the smaller one. */
|
||||
|
||||
if ((size_t) (right_ptr - lo) <= max_thresh)
|
||||
{
|
||||
if ((size_t) (hi - left_ptr) <= max_thresh)
|
||||
/* Ignore both small partitions. */
|
||||
POP (lo, hi);
|
||||
else
|
||||
/* Ignore small left partition. */
|
||||
lo = left_ptr;
|
||||
}
|
||||
else if ((size_t) (hi - left_ptr) <= max_thresh)
|
||||
/* Ignore small right partition. */
|
||||
hi = right_ptr;
|
||||
else if ((right_ptr - lo) > (hi - left_ptr))
|
||||
{
|
||||
/* Push larger left partition indices. */
|
||||
PUSH (lo, right_ptr);
|
||||
lo = left_ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Push larger right partition indices. */
|
||||
PUSH (left_ptr, hi);
|
||||
hi = right_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Once the BASE_PTR array is partially sorted by quicksort the rest
|
||||
is completely sorted using insertion sort, since this is efficient
|
||||
for partitions below MAX_THRESH size. BASE_PTR points to the beginning
|
||||
of the array to sort, and END_PTR points at the very last element in
|
||||
the array (*not* one beyond it!). */
|
||||
|
||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
{
|
||||
char *const end_ptr = &base_ptr[size * (total_elems - 1)];
|
||||
char *tmp_ptr = base_ptr;
|
||||
char *thresh = min(end_ptr, base_ptr + max_thresh);
|
||||
register char *run_ptr;
|
||||
|
||||
/* Find smallest element in first threshold and place it at the
|
||||
array's beginning. This is the smallest array element,
|
||||
and the operation speeds up insertion sort's inner loop. */
|
||||
|
||||
for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size)
|
||||
if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, opaque) < 0)
|
||||
tmp_ptr = run_ptr;
|
||||
|
||||
if (tmp_ptr != base_ptr)
|
||||
SWAP (tmp_ptr, base_ptr, size);
|
||||
|
||||
/* Insertion sort, running from left-hand-side up to right-hand-side. */
|
||||
|
||||
run_ptr = base_ptr + size;
|
||||
while ((run_ptr += size) <= end_ptr)
|
||||
{
|
||||
tmp_ptr = run_ptr - size;
|
||||
while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, opaque) < 0)
|
||||
tmp_ptr -= size;
|
||||
|
||||
tmp_ptr += size;
|
||||
if (tmp_ptr != run_ptr)
|
||||
{
|
||||
char *trav;
|
||||
|
||||
trav = run_ptr + size;
|
||||
while (--trav >= run_ptr)
|
||||
{
|
||||
char c = *trav;
|
||||
char *hi, *lo;
|
||||
|
||||
for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo)
|
||||
*hi = *lo;
|
||||
*hi = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user