1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00

make a more recent snapshot of ldb available to interested

people. Note that I decided to make it LGPL.

ldb is not finished yet, but enough of it is there for people to get
an idea of what it does, and quite a few simple tests work
(This used to be commit dc6f41f9e7)
This commit is contained in:
Andrew Tridgell 2004-03-31 06:45:39 +00:00
parent 4aa785b1b2
commit 58d50a614f
36 changed files with 5656 additions and 0 deletions

View File

@ -0,0 +1,43 @@
CFLAGS=-Wall -g -Iinclude -I. -DSTANDALONE=1 -DUSE_MMAP=1
OPENLDAP=/home/tridge/samba/openldap/prefix
LIB_FLAGS=-Llib -lldb -L$(OPENLDAP)/lib -lldap
TDB_OBJ=tdb/tdb.o tdb/spinlock.o
LDB_TDB_OBJ=ldb_tdb/ldb_match.o ldb_tdb/ldb_tdb.o \
ldb_tdb/ldb_pack.o ldb_tdb/ldb_search.o ldb_tdb/ldb_index.o
LDB_LDAP_OBJ=ldb_ldap/ldb_ldap.o
COMMON_OBJ=common/ldb.o common/ldb_ldif.o common/util.o common/ldb_parse.o
OBJS = $(COMMON_OBJ) $(LDB_TDB_OBJ) $(TDB_OBJ) $(LDB_LDAP_OBJ)
LDB_LIB = lib/libldb.a
BINS = bin/ldbadd bin/ldbsearch bin/ldbdel
LIBS = $(LDB_LIB)($(OBJS))
all: $(BINS) $(LIBS)
lib/libldb.a: $(OBJS)
bin/ldbadd: tools/ldbadd.o $(LIBS)
$(CC) -o bin/ldbadd tools/ldbadd.o $(LIB_FLAGS)
bin/ldbsearch: tools/ldbsearch.o $(LIBS)
$(CC) -o bin/ldbsearch tools/ldbsearch.o $(LIB_FLAGS)
bin/ldbdel: tools/ldbdel.o $(LIBS)
$(CC) -o bin/ldbdel tools/ldbdel.o $(LIB_FLAGS)
clean:
rm -f */*.o *~ */*~ $(BINS) $(LDB_LIB)
proto:
mkproto.pl */*.c > include/proto.h
etags:
etags */*.[ch]

View File

@ -0,0 +1,129 @@
/*
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 core API
*
* Description: core API routines interfacing to ldb backends
*
* Author: Andrew Tridgell
*/
#include "includes.h"
/*
connect to a database. The URL can either be one of the following forms
ldb://path
ldapi://path
flags is made up of LDB_FLG_*
the options are passed uninterpreted to the backend, and are
backend specific
*/
struct ldb_context *ldb_connect(const char *url, unsigned int flags,
const char *options[])
{
if (strncmp(url, "tdb:", 4) == 0) {
return ltdb_connect(url, flags, options);
}
if (strncmp(url, "ldap", 4) == 0) {
return lldb_connect(url, flags, options);
}
errno = EINVAL;
return NULL;
}
/*
close the connection to the database
*/
int ldb_close(struct ldb_context *ldb)
{
return ldb->ops->close(ldb);
}
/*
search the database given a LDAP-like search expression
return the number of records found, or -1 on error
*/
int ldb_search(struct ldb_context *ldb,
const char *base,
enum ldb_scope scope,
const char *expression,
const char *attrs[], struct ldb_message ***res)
{
return ldb->ops->search(ldb, base, scope, expression, attrs, res);
}
/*
free a set of messages returned by ldb_search
*/
int ldb_search_free(struct ldb_context *ldb, struct ldb_message **msgs)
{
return ldb->ops->search_free(ldb, msgs);
}
/*
add a record to the database. Will fail if a record with the given class and key
already exists
*/
int ldb_add(struct ldb_context *ldb,
const struct ldb_message *message)
{
return ldb->ops->add(ldb, message);
}
/*
modify the specified attributes of a record
*/
int ldb_modify(struct ldb_context *ldb,
const struct ldb_message *message)
{
return ldb->ops->modify(ldb, message);
}
/*
delete a record from the database
*/
int ldb_delete(struct ldb_context *ldb, const char *dn)
{
return ldb->ops->delete(ldb, dn);
}
/*
return extended error information
*/
const char *ldb_errstring(struct ldb_context *ldb)
{
return ldb->ops->errstring(ldb);
}

View File

@ -0,0 +1,476 @@
/*
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
*/
#include "includes.h"
/*
this base64 decoder was taken from jitterbug (written by tridge).
we might need to replace it with a new version
*/
static int base64_decode(char *s)
{
const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int bit_offset, byte_offset, idx, i, n;
unsigned char *d = (unsigned char *)s;
char *p;
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 (*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(const char *buf, int len)
{
const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int bit_offset, byte_offset, idx, i;
unsigned char *d = (unsigned char *)buf;
int bytes = (len*8 + 5)/6;
char *out;
out = malloc(bytes+2);
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];
}
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)
{
int i;
unsigned char *p = val->data;
if (val->length == 0 || 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,
const char *buf, size_t length, int start_pos)
{
int i;
int total=0, ret;
for (i=0;i<length;i++) {
ret = fprintf_fn(private, "%c", buf[i]);
CHECK_RET;
if (i != (length-1) && (i + start_pos) % 77 == 0) {
ret = fprintf_fn(private, "\n ");
CHECK_RET;
}
}
return total;
}
/*
encode as base64 to a file
*/
static int base64_encode_f(int (*fprintf_fn)(void *, const char *, ...), void *private,
const char *buf, int len, int start_pos)
{
char *b = ldb_base64_encode(buf, len);
int ret;
if (!b) {
return -1;
}
ret = fold_string(fprintf_fn, private, b, strlen(b), start_pos);
free(b);
return ret;
}
/*
write to ldif, using a caller supplied write method
*/
int ldif_write(int (*fprintf_fn)(void *, const char *, ...),
void *private,
const struct ldb_message *msg)
{
int i;
int total=0, ret;
ret = fprintf_fn(private, "dn: %s\n", msg->dn);
CHECK_RET;
for (i=0;i<msg->num_elements;i++) {
if (ldb_should_b64_encode(&msg->elements[i].value)) {
ret = fprintf_fn(private, "%s:: ", msg->elements[i].name);
CHECK_RET;
ret = base64_encode_f(fprintf_fn, private,
msg->elements[i].value.data,
msg->elements[i].value.length,
strlen(msg->elements[i].name)+3);
CHECK_RET;
ret = fprintf_fn(private, "\n");
CHECK_RET;
} else {
ret = fprintf_fn(private, "%s: ", msg->elements[i].name);
CHECK_RET;
ret = fold_string(fprintf_fn, private,
msg->elements[i].value.data,
msg->elements[i].value.length,
strlen(msg->elements[i].name)+2);
CHECK_RET;
ret = fprintf_fn(private, "\n");
CHECK_RET;
}
}
ret = fprintf_fn(private,"\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(int (*fgetc_fn)(void *), void *private)
{
size_t alloc_size=0, chunk_size = 0;
char *chunk = NULL;
int c;
int in_comment = 0;
while ((c = fgetc_fn(private)) != EOF) {
if (chunk_size == alloc_size) {
char *c2;
alloc_size += 1024;
c2 = realloc_p(chunk, char, alloc_size);
if (!c2) {
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;
}
return chunk;
}
/* simple ldif attribute parser */
static int next_attr(char **s, char **attr, struct ldb_val *value)
{
char *p;
int base64_encoded = 0;
p = strchr(*s, ':');
if (!p) {
return -1;
}
*p++ = 0;
if (*p == ':') {
base64_encoded = 1;
p++;
}
*attr = *s;
while (isspace(*p)) {
p++;
}
value->data = 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 = base64_decode(value->data);
if (len == -1) {
/* it wasn't valid base64 data */
return -1;
}
value->length = len;
}
return 0;
}
/*
free a message from a ldif_read
*/
void ldif_read_free(struct ldb_message *msg)
{
if (msg->elements) free(msg->elements);
if (msg->private) free(msg->private);
free(msg);
}
/*
read from a LDIF source, creating a ldb_message
*/
struct ldb_message *ldif_read(int (*fgetc_fn)(void *), void *private)
{
struct ldb_message *msg;
char *attr=NULL, *chunk=NULL, *s;
struct ldb_val value;
value.data = NULL;
msg = malloc_p(struct ldb_message);
if (!msg) return NULL;
msg->dn = NULL;
msg->elements = NULL;
msg->num_elements = 0;
msg->private = NULL;
chunk = next_chunk(fgetc_fn, private);
if (!chunk) {
goto failed;
}
msg->private = chunk;
s = chunk;
if (next_attr(&s, &attr, &value) != 0) {
goto failed;
}
/* first line must be a dn */
if (strcmp(attr, "dn") != 0) {
fprintf(stderr, "First line must be a dn not '%s'\n", attr);
goto failed;
}
msg->dn = value.data;
while (next_attr(&s, &attr, &value) == 0) {
msg->elements = realloc_p(msg->elements,
struct ldb_message_element,
msg->num_elements+1);
if (!msg->elements) {
goto failed;
}
msg->elements[msg->num_elements].flags = 0;
msg->elements[msg->num_elements].name = attr;
msg->elements[msg->num_elements].value = value;
msg->num_elements++;
}
return msg;
failed:
if (msg) ldif_read_free(msg);
return NULL;
}
/*
a wrapper around ldif_read() for reading from FILE*
*/
struct ldif_read_file_state {
FILE *f;
};
static int fgetc_file(void *private)
{
struct ldif_read_file_state *state = private;
return fgetc(state->f);
}
struct ldb_message *ldif_read_file(FILE *f)
{
struct ldif_read_file_state state;
state.f = f;
return ldif_read(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)
{
struct ldif_read_string_state *state = private;
if (state->s[0] != 0) {
return *state->s++;
}
return EOF;
}
struct ldb_message *ldif_read_string(const char *s)
{
struct ldif_read_string_state state;
state.s = s;
return ldif_read(fgetc_string, &state);
}
/*
wrapper around ldif_write() for a file
*/
struct ldif_write_file_state {
FILE *f;
};
static int fprintf_file(void *private, const char *fmt, ...)
{
struct ldif_write_file_state *state = private;
int ret;
va_list ap;
va_start(ap, fmt);
ret = vfprintf(state->f, fmt, ap);
va_end(ap);
return ret;
}
int ldif_write_file(FILE *f, const struct ldb_message *msg)
{
struct ldif_write_file_state state;
state.f = f;
return ldif_write(fprintf_file, &state, msg);
}

View File

@ -0,0 +1,460 @@
/*
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"
/*
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> ::= '=' | '~=' | '<=' | '>='
*/
/*
return next token element. Caller frees
*/
static char *ldb_parse_lex(const char **s)
{
const char *p = *s;
char *ret;
while (isspace(*p)) {
p++;
}
*s = p;
if (*p == 0) {
return NULL;
}
if (strchr("()&|=!", *p)) {
(*s) = p+1;
ret = strndup(p, 1);
if (!ret) {
errno = ENOMEM;
}
return ret;
}
while (*p && (isalnum(*p) || !strchr("()&|=!", *p))) {
p++;
}
if (p == *s) {
return NULL;
}
ret = strndup(*s, p - *s);
if (!ret) {
errno = ENOMEM;
}
*s = p;
return ret;
}
/*
find a matching close brace in a string
*/
static const char *match_brace(const char *s)
{
unsigned int count = 0;
while (*s && (count != 0 || *s != ')')) {
if (*s == '(') {
count++;
}
if (*s == ')') {
count--;
}
s++;
}
if (! *s) {
return NULL;
}
return s;
}
static struct ldb_parse_tree *ldb_parse_filter(const char **s);
/*
<simple> ::= <attributetype> <filtertype> <attributevalue>
*/
static struct ldb_parse_tree *ldb_parse_simple(const char *s)
{
char *eq, *val, *l;
struct ldb_parse_tree *ret;
l = ldb_parse_lex(&s);
if (!l) {
fprintf(stderr, "Unexpected end of expression\n");
return NULL;
}
if (strchr("()&|=", *l)) {
fprintf(stderr, "Unexpected token '%s'\n", l);
free(l);
return NULL;
}
eq = ldb_parse_lex(&s);
if (!eq || strcmp(eq, "=") != 0) {
fprintf(stderr, "Expected '='\n");
free(l);
if (eq) free(eq);
return NULL;
}
free(eq);
val = ldb_parse_lex(&s);
if (val && strchr("()&|=", *val)) {
fprintf(stderr, "Unexpected token '%s'\n", val);
free(l);
if (val) free(val);
return NULL;
}
ret = malloc_p(struct ldb_parse_tree);
if (!ret) {
errno = ENOMEM;
return NULL;
}
ret->operation = LDB_OP_SIMPLE;
ret->u.simple.attr = l;
ret->u.simple.value.data = val;
ret->u.simple.value.length = val?strlen(val):0;
return ret;
}
/*
parse a filterlist
<and> ::= '&' <filterlist>
<or> ::= '|' <filterlist>
<filterlist> ::= <filter> | <filter> <filterlist>
*/
static struct ldb_parse_tree *ldb_parse_filterlist(enum ldb_parse_op op, const char *s)
{
struct ldb_parse_tree *ret, *next;
ret = malloc_p(struct ldb_parse_tree);
if (!ret) {
errno = ENOMEM;
return NULL;
}
ret->operation = op;
ret->u.list.num_elements = 1;
ret->u.list.elements = malloc_p(struct ldb_parse_tree *);
if (!ret->u.list.elements) {
errno = ENOMEM;
free(ret);
return NULL;
}
ret->u.list.elements[0] = ldb_parse_filter(&s);
if (!ret->u.list.elements[0]) {
free(ret->u.list.elements);
free(ret);
return NULL;
}
while (isspace(*s)) s++;
while (*s && (next = ldb_parse_filter(&s))) {
struct ldb_parse_tree **e;
e = realloc_p(ret->u.list.elements,
struct ldb_parse_tree *,
ret->u.list.num_elements+1);
if (!e) {
errno = ENOMEM;
ldb_parse_tree_free(next);
ldb_parse_tree_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(*s)) s++;
}
return ret;
}
/*
<not> ::= '!' <filter>
*/
static struct ldb_parse_tree *ldb_parse_not(const char *s)
{
struct ldb_parse_tree *ret;
ret = malloc_p(struct ldb_parse_tree);
if (!ret) {
errno = ENOMEM;
return NULL;
}
ret->operation = LDB_OP_NOT;
ret->u.not.child = ldb_parse_filter(&s);
if (!ret->u.not.child) {
free(ret);
return NULL;
}
return ret;
}
/*
parse a filtercomp
<filtercomp> ::= <and> | <or> | <not> | <simple>
*/
static struct ldb_parse_tree *ldb_parse_filtercomp(const char *s)
{
while (isspace(*s)) s++;
switch (*s) {
case '&':
return ldb_parse_filterlist(LDB_OP_AND, s+1);
case '|':
return ldb_parse_filterlist(LDB_OP_OR, s+1);
case '!':
return ldb_parse_not(s+1);
case '(':
case ')':
fprintf(stderr, "Unexpected token '%c'\n", *s);
return NULL;
}
return ldb_parse_simple(s);
}
/*
<filter> ::= '(' <filtercomp> ')'
*/
static struct ldb_parse_tree *ldb_parse_filter(const char **s)
{
char *l, *s2;
const char *p, *p2;
struct ldb_parse_tree *ret;
l = ldb_parse_lex(s);
if (!l) {
fprintf(stderr, "Unexpected end of expression\n");
return NULL;
}
if (strcmp(l, "(") != 0) {
free(l);
fprintf(stderr, "Expected '('\n");
return NULL;
}
free(l);
p = match_brace(*s);
if (!p) {
fprintf(stderr, "Parse error - mismatched braces\n");
return NULL;
}
p2 = p + 1;
s2 = strndup(*s, p - *s);
if (!s2) {
errno = ENOMEM;
return NULL;
}
ret = ldb_parse_filtercomp(s2);
free(s2);
*s = p2;
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(const char *s)
{
while (isspace(*s)) s++;
if (*s == '(') {
return ldb_parse_filter(&s);
}
return ldb_parse_simple(s);
}
/*
free a parse tree returned from ldb_parse_tree()
*/
void ldb_parse_tree_free(struct ldb_parse_tree *tree)
{
int i;
switch (tree->operation) {
case LDB_OP_SIMPLE:
free(tree->u.simple.attr);
if (tree->u.simple.value.data) free(tree->u.simple.value.data);
break;
case LDB_OP_AND:
case LDB_OP_OR:
for (i=0;i<tree->u.list.num_elements;i++) {
ldb_parse_tree_free(tree->u.list.elements[i]);
}
if (tree->u.list.elements) free(tree->u.list.elements);
break;
case LDB_OP_NOT:
ldb_parse_tree_free(tree->u.not.child);
break;
}
free(tree);
}
#if TEST_PROGRAM
/*
return a string representation of a parse tree
used for debugging
*/
static char *tree_string(struct ldb_parse_tree *tree)
{
char *s = NULL;
char *s1, *s2;
int i;
switch (tree->operation) {
case LDB_OP_SIMPLE:
asprintf(&s, "( %s = \"%s\" )", tree->u.simple.attr,
(char *)tree->u.simple.value.data);
break;
case LDB_OP_AND:
case LDB_OP_OR:
asprintf(&s, "( %c", tree->operation==LDB_OP_AND?'&':'|');
if (!s) return NULL;
for (i=0;i<tree->u.list.num_elements;i++) {
s1 = tree_string(tree->u.list.elements[i]);
if (!s1) {
free(s);
return NULL;
}
asprintf(&s2, "%s %s", s, s1);
free(s);
free(s1);
s = s2;
}
if (!s) {
return NULL;
}
asprintf(&s2, "%s )", s);
free(s);
s = s2;
break;
case LDB_OP_NOT:
s1 = tree_string(tree->u.not.child);
asprintf(&s, "( ! %s )", s1);
free(s1);
break;
}
return s;
}
/*
print a tree
*/
static void print_tree(struct ldb_parse_tree *tree)
{
char *s = tree_string(tree);
printf("%s\n", s);
free(s);
}
int main(void)
{
char line[1000];
int ret = 0;
while (fgets(line, sizeof(line)-1, stdin)) {
struct ldb_parse_tree *tree;
if (line[strlen(line)-1] == '\n') {
line[strlen(line)-1] = 0;
}
tree = ldb_parse_tree(line);
if (!tree) {
fprintf(stderr, "Failed to parse\n");
ret = 1;
continue;
}
print_tree(tree);
ldb_parse_tree_free(tree);
}
return ret;
}
#endif /* TEST_PROGRAM */

View File

@ -0,0 +1,102 @@
/*
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 utility functions
*
* Description: miscellanous utility functions for ldb
*
* Author: Andrew Tridgell
*/
#include "includes.h"
#define MAX_MALLOC_SIZE 0x7fffffff
/*
realloc an array, checking for integer overflow in the array size
*/
void *realloc_array(void *ptr, size_t el_size, unsigned count)
{
if (count == 0 ||
count >= MAX_MALLOC_SIZE/el_size) {
return NULL;
}
if (!ptr) {
return malloc(el_size * count);
}
return realloc(ptr, el_size * count);
}
/*
find an element in a list, using the given comparison function and
assuming that the list is already sorted using comp_fn
return -1 if not found, or the index of the first occurance of needle if found
*/
int list_find(const void *needle,
const void *base, size_t nmemb, size_t size, comparison_fn_t comp_fn)
{
const char *base_p = base;
size_t min_i, max_i, test_i;
if (nmemb == 0) {
return -1;
}
min_i = 0;
max_i = nmemb-1;
while (min_i < max_i) {
size_t test_t;
int r;
test_i = (min_i + max_i) / 2;
r = comp_fn(needle, *(void **)(base_p + (size * test_i)));
if (r == 0) {
/* scan back for first element */
while (test_t > 0 &&
comp_fn(needle, *(void **)(base_p + (size * (test_i-1)))) == 0) {
test_i--;
}
return test_i;
}
if (r == -1) {
max_i = test_i - 1;
}
if (r == 1) {
min_i = test_i + 1;
}
}
if (comp_fn(needle, *(void **)(base_p + (size * min_i))) == 0) {
return min_i;
}
return -1;
}

View File

@ -0,0 +1,41 @@
The list of indexed fields
--------------------------
dn=@INDEXLIST
list of field names that are indexed
contains fields of type @IDXATTR which contain attriute names
of indexed fields
Data records
------------
for each user record in the db there is:
main record
key: DN=dn
data: packed attribute/value list
a index record for each indexed field in the record
Index Records
-------------
The index records contain the list of dn's that contain records
matching the index key
All index records are of the form:
dn=@INDEX:field:value
and contain fields of type @IDX which are the dns of the records
that have that value for some attribute
Search Expressions
------------------
Very similar to LDAP search expressions, but does not allow ~=, <= or >=
attrib0 := (field=value)
attrib := attrib0 | (attrib&&attrib) | (attrib||attrib) | !attrib

View File

@ -0,0 +1,22 @@
/*
a temporary includes file until I work on the ldb build system
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdarg.h>
#include <signal.h>
#include "ldb.h"
#include "ldb_parse.h"
#define malloc_p(type) (type *)malloc(sizeof(type))
#define malloc_array_p(type, count) (type *)realloc_array(NULL, sizeof(type), count)
#define realloc_p(p, type, count) (type *)realloc_array(p, sizeof(type), count)
#include "tdb/tdb.h"
#include "proto.h"

View File

@ -0,0 +1,204 @@
/*
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 header
*
* Description: defines for base ldb API
*
* Author: Andrew Tridgell
*/
/*
major restrictions as compared to normal LDAP:
- no async calls.
- each record must have a unique key field
- the key must be representable as a NULL terminated C string and may not
contain a comma or braces
major restrictions as compared to tdb:
- no explicit locking calls
*/
/*
an individual lump of data in a result comes in this format. The
pointer will usually be to a UTF-8 string if the application is
sensible, but it can be to anything you like, including binary data
blobs of arbitrary size.
*/
struct ldb_val {
unsigned int length;
void *data;
};
/* these flags are used in ldd_message_element.flags fields. The
LDA_FLAGS_MOD_* flags are used in ldap_modify() calls to specify
whether attributes are being added, deleted or modified */
#define LDB_FLAG_MOD_MASK 0x3
#define LDB_FLAG_MOD_ADD 1
#define LDB_FLAG_MOD_REPLACE 2
#define LDB_FLAG_MOD_DELETE 3
/*
results are given back as arrays of ldb_message_element
*/
struct ldb_message_element {
unsigned int flags;
char *name;
struct ldb_val value;
};
/*
a ldb_message represents all or part of a record. It can contain an arbitrary
number of elements.
*/
struct ldb_message {
char *dn;
unsigned int num_elements;
struct ldb_message_element *elements;
void *private; /* private to the backend */
};
enum ldb_scope {LDB_SCOPE_DEFAULT=-1,
LDB_SCOPE_BASE=0,
LDB_SCOPE_ONELEVEL=1,
LDB_SCOPE_SUBTREE=2};
struct ldb_context;
/*
the fuction type for the callback used in traversing the database
*/
typedef int (*ldb_traverse_fn)(struct ldb_context *, const struct ldb_message *);
/*
these function pointers define the operations that a ldb backend must perform
they correspond exactly to the ldb_*() interface
*/
struct ldb_backend_ops {
int (*close)(struct ldb_context *);
int (*search)(struct ldb_context *, const char *, enum ldb_scope,
const char *, const char *[], struct ldb_message ***);
int (*search_free)(struct ldb_context *, struct ldb_message **);
int (*add)(struct ldb_context *, const struct ldb_message *);
int (*modify)(struct ldb_context *, const struct ldb_message *);
int (*delete)(struct ldb_context *, const char *);
const char * (*errstring)(struct ldb_context *);
};
/*
every ldb connection is started by establishing a ldb_context
*/
struct ldb_context {
/* a private pointer for the backend to use */
void *private;
/* the operations provided by the backend */
const struct ldb_backend_ops *ops;
};
#define LDB_FLG_RDONLY 1
/*
connect to a database. The URL can either be one of the following forms
ldb://path
ldapi://path
flags is made up of LDB_FLG_*
the options are passed uninterpreted to the backend, and are
backend specific
*/
struct ldb_context *ldb_connect(const char *url, unsigned int flags,
const char *options[]);
/*
close the connection to the database
*/
int ldb_close(struct ldb_context *ldb);
/*
search the database given a LDAP-like search expression
return the number of records found, or -1 on error
*/
int ldb_search(struct ldb_context *ldb,
const char *base,
enum ldb_scope scope,
const char *expression,
const char *attrs[], struct ldb_message ***res);
/*
free a set of messages returned by ldb_search
*/
int ldb_search_free(struct ldb_context *ldb, struct ldb_message **msgs);
/*
add a record to the database. Will fail if a record with the given class and key
already exists
*/
int ldb_add(struct ldb_context *ldb,
const struct ldb_message *message);
/*
modify the specified attributes of a record
*/
int ldb_modify(struct ldb_context *ldb,
const struct ldb_message *message);
/*
delete a record from the database
*/
int ldb_delete(struct ldb_context *ldb, const char *dn);
/*
return extended error information from the last call
*/
const char *ldb_errstring(struct ldb_context *ldb);
/*
ldif manipulation functions
*/
int ldif_write(int (*fprintf_fn)(void *, const char *, ...),
void *private,
const struct ldb_message *msg);
void ldif_read_free(struct ldb_message *msg);
struct ldb_message *ldif_read(int (*fgetc_fn)(void *), void *private);
struct ldb_message *ldif_read_file(FILE *f);
struct ldb_message *ldif_read_string(const char *s);
int ldif_write_file(FILE *f, const struct ldb_message *msg);

View File

@ -0,0 +1,53 @@
/*
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 parse header
*
* Description: structure for expression parsing
*
* Author: Andrew Tridgell
*/
enum ldb_parse_op {LDB_OP_SIMPLE, LDB_OP_AND, LDB_OP_OR, LDB_OP_NOT};
struct ldb_parse_tree {
enum ldb_parse_op operation;
union {
struct {
char *attr;
struct ldb_val value;
} simple;
struct {
unsigned int num_elements;
struct ldb_parse_tree **elements;
} list;
struct {
struct ldb_parse_tree *child;
} not;
} u;
};

View File

@ -0,0 +1,524 @@
/*
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 ldap backend
*
* Description: core files for LDAP backend
*
* Author: Andrew Tridgell
*/
#include "includes.h"
#include "ldb_ldap/ldb_ldap.h"
#if 0
/*
we don't need this right now, but will once we add more backend
options
*/
/*
find an option in an option list (a null terminated list of strings)
this assumes the list is short. If it ever gets long then we really
should do this in some smarter way
*/
static const char *lldb_option_find(const struct lldb_private *lldb, const char *name)
{
int i;
size_t len = strlen(name);
if (!lldb->options) return NULL;
for (i=0;lldb->options[i];i++) {
if (strncmp(lldb->options[i], name, len) == 0 &&
lldb->options[i][len] == '=') {
return &lldb->options[i][len+1];
}
}
return NULL;
}
#endif
/*
close/free the connection
*/
static int lldb_close(struct ldb_context *ldb)
{
int i, ret = 0;
struct lldb_private *lldb = ldb->private;
if (ldap_unbind(lldb->ldap) != LDAP_SUCCESS) {
ret = -1;
}
if (lldb->options) {
for (i=0;lldb->options[i];i++) {
free(lldb->options[i]);
}
free(lldb->options);
}
free(lldb);
free(ldb);
return ret;
}
/*
delete a record
*/
static int lldb_delete(struct ldb_context *ldb, const char *dn)
{
struct lldb_private *lldb = ldb->private;
int ret = 0;
lldb->last_rc = ldap_delete_s(lldb->ldap, dn);
if (lldb->last_rc != LDAP_SUCCESS) {
ret = -1;
}
return ret;
}
/*
free a search message
*/
static int lldb_msg_free(struct ldb_context *ldb, struct ldb_message *msg)
{
int i;
free(msg->dn);
for (i=0;i<msg->num_elements;i++) {
free(msg->elements[i].name);
if (msg->elements[i].value.data) {
free(msg->elements[i].value.data);
}
}
if (msg->elements) free(msg->elements);
free(msg);
return 0;
}
/*
free a search result
*/
static int lldb_search_free(struct ldb_context *ldb, struct ldb_message **res)
{
int i;
for (i=0;res[i];i++) {
if (lldb_msg_free(ldb, res[i]) != 0) {
return -1;
}
}
free(res);
return 0;
}
/*
add a single set of ldap message values to a ldb_message
*/
static int lldb_add_msg_attr(struct ldb_message *msg,
const char *attr, struct berval **bval)
{
int count, i;
struct ldb_message_element *el;
count = ldap_count_values_len(bval);
if (count <= 0) {
return -1;
}
el = realloc_p(msg->elements, struct ldb_message_element,
msg->num_elements + count);
if (!el) {
errno = ENOMEM;
return -1;
}
msg->elements = el;
for (i=0;i<count;i++) {
msg->elements[msg->num_elements].name = strdup(attr);
if (!msg->elements[msg->num_elements].name) {
return -1;
}
msg->elements[msg->num_elements].value.data = malloc(bval[i]->bv_len);
if (!msg->elements[msg->num_elements].value.data) {
free(msg->elements[msg->num_elements].name);
return -1;
}
memcpy(msg->elements[msg->num_elements].value.data,
bval[i]->bv_val, bval[i]->bv_len);
msg->elements[msg->num_elements].value.length = bval[i]->bv_len;
msg->num_elements++;
}
return 0;
}
/*
search for matching records
*/
static int lldb_search(struct ldb_context *ldb, const char *base,
enum ldb_scope scope, const char *expression,
const char **attrs, struct ldb_message ***res)
{
struct lldb_private *lldb = ldb->private;
int count, msg_count;
LDAPMessage *ldapres, *msg;
lldb->last_rc = ldap_search_s(lldb->ldap, base, (int)scope,
expression, attrs, 0, &ldapres);
if (lldb->last_rc != LDAP_SUCCESS) {
return -1;
}
count = ldap_count_entries(lldb->ldap, ldapres);
if (count == -1 || count == 0) {
ldap_msgfree(ldapres);
return count;
}
(*res) = malloc_array_p(struct ldb_message *, count+1);
if (! *res) {
ldap_msgfree(ldapres);
errno = ENOMEM;
return -1;
}
(*res)[0] = NULL;
msg_count = 0;
/* loop over all messages */
for (msg=ldap_first_entry(lldb->ldap, ldapres);
msg;
msg=ldap_next_entry(lldb->ldap, msg)) {
BerElement *berptr = NULL;
char *attr, *dn;
if (msg_count == count) {
/* hmm, got too many? */
fprintf(stderr,"Too many messages?!\n");
break;
}
(*res)[msg_count] = malloc_p(struct ldb_message);
if (!(*res)[msg_count]) {
goto failed;
}
(*res)[msg_count+1] = NULL;
dn = ldap_get_dn(lldb->ldap, msg);
if (!dn) {
goto failed;
}
(*res)[msg_count]->dn = strdup(dn);
ldap_memfree(dn);
if (!(*res)[msg_count]->dn) {
goto failed;
}
(*res)[msg_count]->num_elements = 0;
(*res)[msg_count]->elements = NULL;
(*res)[msg_count]->private = NULL;
/* loop over all attributes */
for (attr=ldap_first_attribute(lldb->ldap, msg, &berptr);
attr;
attr=ldap_next_attribute(lldb->ldap, msg, berptr)) {
struct berval **bval;
bval = ldap_get_values_len(lldb->ldap, msg, attr);
if (bval) {
lldb_add_msg_attr((*res)[msg_count], attr, bval);
ldap_value_free_len(bval);
}
ldap_memfree(attr);
}
if (berptr) ber_free(berptr, 0);
msg_count++;
}
ldap_msgfree(ldapres);
return msg_count;
failed:
if (*res) lldb_search_free(ldb, *res);
return -1;
}
/*
free a set of mods from lldb_msg_to_mods()
*/
static void lldb_mods_free(LDAPMod **mods)
{
int i, j;
if (!mods) return;
for (i=0;mods[i];i++) {
if (mods[i]->mod_vals.modv_bvals) {
for (j=0;mods[i]->mod_vals.modv_bvals[j];j++) {
free(mods[i]->mod_vals.modv_bvals[j]);
}
free(mods[i]->mod_vals.modv_bvals);
}
free(mods[i]);
}
free(mods);
}
/*
convert a ldb_message structure to a list of LDAPMod structures
ready for ldap_add() or ldap_modify()
*/
static LDAPMod **lldb_msg_to_mods(const struct ldb_message *msg, int use_flags)
{
LDAPMod **mods;
int i, num_vals, num_mods = 0;
/* allocate maximum number of elements needed */
mods = malloc_array_p(LDAPMod *, msg->num_elements+1);
if (!mods) {
errno = ENOMEM;
return NULL;
}
mods[0] = NULL;
for (i=0;i<msg->num_elements;i++) {
if (i > 0 &&
(!use_flags ||
(msg->elements[i].flags == msg->elements[i-1].flags)) &&
strcmp(msg->elements[i].name, msg->elements[i-1].name) == 0) {
struct berval **b;
/* when attributes are repeated we need to extend the
existing bvals array */
b = realloc_p(mods[num_mods-1]->mod_vals.modv_bvals,
struct berval *, num_vals+2);
if (!b) {
goto failed;
}
mods[num_mods-1]->mod_vals.modv_bvals = b;
b[num_vals+1] = NULL;
b[num_vals] = malloc_p(struct berval);
if (!b[num_vals]) goto failed;
b[num_vals]->bv_val = msg->elements[i].value.data;
b[num_vals]->bv_len = msg->elements[i].value.length;
num_vals++;
continue;
}
num_vals = 1;
mods[num_mods] = malloc_p(LDAPMod);
if (!mods[num_mods]) {
goto failed;
}
mods[num_mods+1] = NULL;
mods[num_mods]->mod_op = LDAP_MOD_BVALUES;
if (use_flags) {
switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) {
case LDB_FLAG_MOD_ADD:
mods[num_mods]->mod_op |= LDAP_MOD_ADD;
break;
case LDB_FLAG_MOD_DELETE:
mods[num_mods]->mod_op |= LDAP_MOD_DELETE;
break;
case LDB_FLAG_MOD_REPLACE:
mods[num_mods]->mod_op |= LDAP_MOD_REPLACE;
break;
}
}
mods[num_mods]->mod_type = msg->elements[i].name;
mods[num_mods]->mod_vals.modv_bvals = malloc_array_p(struct berval *, 2);
if (!mods[num_mods]->mod_vals.modv_bvals) {
goto failed;
}
mods[num_mods]->mod_vals.modv_bvals[0] = malloc_p(struct berval);
if (!mods[num_mods]->mod_vals.modv_bvals[0]) {
goto failed;
}
mods[num_mods]->mod_vals.modv_bvals[0]->bv_val = msg->elements[i].value.data;
mods[num_mods]->mod_vals.modv_bvals[0]->bv_len = msg->elements[i].value.length;
mods[num_mods]->mod_vals.modv_bvals[1] = NULL;
num_mods++;
}
return mods;
failed:
lldb_mods_free(mods);
return NULL;
}
/*
add a record
*/
static int lldb_add(struct ldb_context *ldb, const struct ldb_message *msg)
{
struct lldb_private *lldb = ldb->private;
LDAPMod **mods;
int ret = 0;
mods = lldb_msg_to_mods(msg, 0);
lldb->last_rc = ldap_add_s(lldb->ldap, msg->dn, mods);
if (lldb->last_rc != LDAP_SUCCESS) {
ret = -1;
}
lldb_mods_free(mods);
return ret;
}
/*
modify a record
*/
static int lldb_modify(struct ldb_context *ldb, const struct ldb_message *msg)
{
struct lldb_private *lldb = ldb->private;
LDAPMod **mods;
int ret = 0;
mods = lldb_msg_to_mods(msg, 1);
lldb->last_rc = ldap_modify_s(lldb->ldap, msg->dn, mods);
if (lldb->last_rc != LDAP_SUCCESS) {
ret = -1;
}
lldb_mods_free(mods);
return ret;
}
/*
return extended error information
*/
static const char *lldb_errstring(struct ldb_context *ldb)
{
struct lldb_private *lldb = ldb->private;
return ldap_err2string(lldb->last_rc);
}
static const struct ldb_backend_ops lldb_ops = {
lldb_close,
lldb_search,
lldb_search_free,
lldb_add,
lldb_modify,
lldb_delete,
lldb_errstring
};
/*
connect to the database
*/
struct ldb_context *lldb_connect(const char *url,
unsigned int flags,
const char *options[])
{
struct ldb_context *ldb = NULL;
struct lldb_private *lldb = NULL;
int i;
ldb = malloc_p(struct ldb_context);
if (!ldb) {
errno = ENOMEM;
goto failed;
}
lldb = malloc_p(struct lldb_private);
if (!lldb) {
free(ldb);
errno = ENOMEM;
goto failed;
}
lldb->ldap = NULL;
lldb->options = NULL;
lldb->last_rc = ldap_initialize(&lldb->ldap, url);
if (lldb->last_rc != LDAP_SUCCESS) {
goto failed;
}
ldb->ops = &lldb_ops;
ldb->private = lldb;
if (options) {
/* take a copy of the options array, so we don't have to rely
on the caller keeping it around (it might be dynamic) */
for (i=0;options[i];i++) ;
lldb->options = malloc_array_p(char *, i+1);
if (!lldb->options) {
goto failed;
}
for (i=0;options[i];i++) {
lldb->options[i+1] = NULL;
lldb->options[i] = strdup(options[i]);
if (!lldb->options[i]) {
goto failed;
}
}
}
return ldb;
failed:
if (lldb && lldb->options) {
for (i=0;lldb->options[i];i++) {
free(lldb->options[i]);
}
free(lldb->options);
}
if (lldb && lldb->ldap) {
ldap_unbind(lldb->ldap);
}
if (lldb) free(lldb);
if (ldb) free(ldb);
return NULL;
}

View File

@ -0,0 +1,8 @@
#include <ldap.h>
struct lldb_private {
char **options;
const char *basedn;
LDAP *ldap;
int last_rc;
};

View File

@ -0,0 +1,7 @@
ldbadd
ldbsearch
ldbdel
test.ldb
TAGS
.*~
*.o

View File

@ -0,0 +1,641 @@
/*
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 tdb backend - indexing
*
* Description: indexing routines for ldb tdb backend
*
* Author: Andrew Tridgell
*/
#include "includes.h"
struct dn_list {
unsigned int count;
char **dn;
};
/*
free a struct dn_list
*/
static void dn_list_free(struct dn_list *list)
{
int i;
for (i=0;i<list->count;i++) {
free(list->dn[i]);
}
if (list->dn) free(list->dn);
}
/*
return the dn key to be used for an index
caller frees
*/
static char *ldb_dn_key(const char *attr, const struct ldb_val *value)
{
char *ret = NULL;
if (ldb_should_b64_encode(value)) {
char *vstr = ldb_base64_encode(value->data, value->length);
if (!vstr) return NULL;
asprintf(&ret, "@INDEX:%s::%s", attr, vstr);
free(vstr);
return ret;
}
asprintf(&ret, "@INDEX:%s:%s", attr, (char *)value->data);
return ret;
}
/*
see if a attribute value is in the list of indexed attributes
*/
static int ldb_msg_find_idx(const struct ldb_message *msg, const char *attr)
{
int i;
for (i=0;i<msg->num_elements;i++) {
if (strcmp(msg->elements[i].name, "@IDXATTR") == 0 &&
strcmp((char *)msg->elements[i].value.data, attr) == 0) {
return i;
}
}
return -1;
}
/*
return a list of dn's that might match a simple indexed search or
*/
static int ltdb_index_dn_simple(struct ldb_context *ldb,
struct ldb_parse_tree *tree,
const struct ldb_message *index_list,
struct dn_list *list)
{
char *dn = NULL;
int ret, i;
struct ldb_message msg;
list->count = 0;
list->dn = NULL;
/*
if the value is a wildcard then we can't do a match via indexing
*/
if (ltdb_has_wildcard(&tree->u.simple.value)) {
return -1;
}
/* if the attribute isn't in the list of indexed attributes then
this node needs a full search */
if (ldb_msg_find_idx(index_list, tree->u.simple.attr) == -1) {
return -1;
}
/* the attribute is indexed. Pull the list of DNs that match the
search criterion */
dn = ldb_dn_key(tree->u.simple.attr, &tree->u.simple.value);
if (!dn) return -1;
ret = ltdb_search_dn1(ldb, dn, &msg);
free(dn);
if (ret == 0 || ret == -1) {
return ret;
}
list->dn = malloc_array_p(char *, msg.num_elements);
if (!list->dn) {
ltdb_search_dn1_free(ldb, &msg);
}
for (i=0;i<msg.num_elements;i++) {
if (strcmp(msg.elements[i].name, "@IDX") != 0) {
continue;
}
list->dn[list->count] =
strdup((char *)msg.elements[i].value.data);
if (!list->dn[list->count]) {
dn_list_free(list);
ltdb_search_dn1_free(ldb, &msg);
return -1;
}
list->count++;
}
ltdb_search_dn1_free(ldb, &msg);
qsort(list->dn, list->count, sizeof(char *), (comparison_fn_t) strcmp);
return 1;
}
/*
list intersection
list = list & list2
relies on the lists being sorted
*/
static int list_intersect(struct dn_list *list, const struct dn_list *list2)
{
struct dn_list list3;
int i;
if (list->count == 0 || list2->count == 0) {
/* 0 & X == 0 */
dn_list_free(list);
return 0;
}
list3.dn = malloc_array_p(char *, list->count);
if (!list3.dn) {
dn_list_free(list);
return -1;
}
list3.count = 0;
for (i=0;i<list->count;i++) {
if (list_find(list->dn[i], list2->dn, list2->count,
sizeof(char *), (comparison_fn_t)strcmp) != -1) {
list3.dn[list3.count] = list->dn[i];
list3.count++;
} else {
free(list->dn[i]);
}
}
free(list->dn);
list->dn = list3.dn;
list->count = list3.count;
return 0;
}
/*
list union
list = list | list2
relies on the lists being sorted
*/
static int list_union(struct dn_list *list, const struct dn_list *list2)
{
int i;
char **d;
unsigned int count = list->count;
if (list->count == 0 && list2->count == 0) {
/* 0 | 0 == 0 */
dn_list_free(list);
return 0;
}
d = realloc_p(list->dn, char *, list->count + list2->count);
if (!d) {
dn_list_free(list);
return -1;
}
list->dn = d;
for (i=0;i<list2->count;i++) {
if (list_find(list2->dn[i], list->dn, count,
sizeof(char *), (comparison_fn_t)strcmp) == -1) {
list->dn[list->count] = strdup(list2->dn[i]);
if (!list->dn[list->count]) {
dn_list_free(list);
return -1;
}
list->count++;
}
}
if (list->count != count) {
qsort(list->dn, list->count, sizeof(char *), (comparison_fn_t)strcmp);
}
return 0;
}
static int ltdb_index_dn(struct ldb_context *ldb,
struct ldb_parse_tree *tree,
const struct ldb_message *index_list,
struct dn_list *list);
/*
OR two index results
*/
static int ltdb_index_dn_or(struct ldb_context *ldb,
struct ldb_parse_tree *tree,
const struct ldb_message *index_list,
struct dn_list *list)
{
int ret, i;
ret = -1;
list->dn = NULL;
list->count = 0;
for (i=0;i<tree->u.list.num_elements;i++) {
struct dn_list list2;
int v;
v = ltdb_index_dn(ldb, tree->u.list.elements[i], index_list, &list2);
if (v == 0) {
/* 0 || X == X */
if (ret == -1) {
ret = 0;
}
continue;
}
if (v == -1) {
/* 1 || X == 1 */
dn_list_free(list);
return -1;
}
if (ret == -1) {
ret = 1;
*list = list2;
} else {
if (list_union(list, &list2) == -1) {
dn_list_free(&list2);
return -1;
}
dn_list_free(&list2);
}
}
if (list->count == 0) {
dn_list_free(list);
return 0;
}
return ret;
}
/*
NOT an index results
*/
static int ltdb_index_dn_not(struct ldb_context *ldb,
struct ldb_parse_tree *tree,
const struct ldb_message *index_list,
struct dn_list *list)
{
/* the only way to do an indexed not would be if we could
negate the not via another not or if we knew the total
number of database elements so we could know that the
existing expression covered the whole database.
instead, we just give up, and rely on a full index scan
(unless an outer & manages to reduce the list)
*/
return -1;
}
/*
AND two index results
*/
static int ltdb_index_dn_and(struct ldb_context *ldb,
struct ldb_parse_tree *tree,
const struct ldb_message *index_list,
struct dn_list *list)
{
int ret, i;
ret = -1;
list->dn = NULL;
list->count = 0;
for (i=0;i<tree->u.list.num_elements;i++) {
struct dn_list list2;
int v;
v = ltdb_index_dn(ldb, tree->u.list.elements[i], index_list, &list2);
if (v == 0) {
/* 0 && X == 0 */
dn_list_free(list);
return 0;
}
if (v == -1) {
continue;
}
if (ret == -1) {
ret = 1;
*list = list2;
} else {
if (list_intersect(list, &list2) == -1) {
dn_list_free(&list2);
return -1;
}
dn_list_free(&list2);
}
if (list->count == 0) {
if (list->dn) free(list->dn);
return 0;
}
}
return ret;
}
/*
return a list of dn's that might match a indexed search or
-1 if an error. return 0 for no matches, or 1 for matches
*/
static int ltdb_index_dn(struct ldb_context *ldb,
struct ldb_parse_tree *tree,
const struct ldb_message *index_list,
struct dn_list *list)
{
int ret;
switch (tree->operation) {
case LDB_OP_SIMPLE:
ret = ltdb_index_dn_simple(ldb, tree, index_list, list);
break;
case LDB_OP_AND:
ret = ltdb_index_dn_and(ldb, tree, index_list, list);
break;
case LDB_OP_OR:
ret = ltdb_index_dn_or(ldb, tree, index_list, list);
break;
case LDB_OP_NOT:
ret = ltdb_index_dn_not(ldb, tree, index_list, list);
break;
}
return ret;
}
/*
filter a candidate dn_list from an indexed search into a set of results
extracting just the given attributes
*/
static int ldb_index_filter(struct ldb_context *ldb, struct ldb_parse_tree *tree,
const char *base,
enum ldb_scope scope,
const struct dn_list *dn_list,
const char *attrs[], struct ldb_message ***res)
{
int i;
unsigned int count = 0;
for (i=0;i<dn_list->count;i++) {
struct ldb_message msg;
int ret;
ret = ltdb_search_dn1(ldb, dn_list->dn[i], &msg);
if (ret == 0) {
/* the record has disappeared? yes, this can happen */
continue;
}
if (ret == -1) {
/* an internal error */
return -1;
}
if (ldb_message_match(ldb, &msg, tree, base, scope) == 1) {
ret = ltdb_add_attr_results(ldb, &msg, attrs, &count, res);
}
ltdb_search_dn1_free(ldb, &msg);
if (ret != 0) {
return -1;
}
}
return count;
}
/*
search the database with a LDAP-like expression using indexes
returns -1 if an indexed search is not possible, in which
case the caller should call ltdb_search_full()
*/
int ltdb_search_indexed(struct ldb_context *ldb,
const char *base,
enum ldb_scope scope,
struct ldb_parse_tree *tree,
const char *attrs[], struct ldb_message ***res)
{
struct ldb_message index_list;
struct dn_list dn_list;
int ret;
/* find the list of indexed fields */
ret = ltdb_search_dn1(ldb, "@INDEXLIST", &index_list);
if (ret != 1) {
/* no index list? must do full search */
return -1;
}
ret = ltdb_index_dn(ldb, tree, &index_list, &dn_list);
ltdb_search_dn1_free(ldb, &index_list);
if (ret == 1) {
/* we've got a candidate list - now filter by the full tree
and extract the needed attributes */
ret = ldb_index_filter(ldb, tree, base, scope, &dn_list,
attrs, res);
dn_list_free(&dn_list);
}
return ret;
}
/*
add an index entry for one message element
*/
static int ltdb_index_add1(struct ldb_context *ldb, const char *dn,
struct ldb_message_element *el)
{
struct ldb_message msg;
char *dn_key;
int ret;
struct ldb_message_element *el2;
dn_key = ldb_dn_key(el->name, &el->value);
if (!dn_key) {
return -1;
}
ret = ltdb_search_dn1(ldb, dn_key, &msg);
if (ret == -1) {
free(dn_key);
return -1;
}
if (ret == 0) {
msg.dn = dn_key;
msg.num_elements = 0;
msg.elements = NULL;
msg.private = NULL;
}
/* add another entry */
el2 = realloc_p(msg.elements, struct ldb_message_element, msg.num_elements+1);
if (!el2) {
if (ret == 1) {
ltdb_search_dn1_free(ldb, &msg);
}
free(dn_key);
return -1;
}
msg.elements = el2;
msg.elements[msg.num_elements].name = "@IDX";
msg.elements[msg.num_elements].value.length = strlen(dn);
msg.elements[msg.num_elements].value.data = dn;
msg.num_elements++;
ret = ltdb_store(ldb, &msg, TDB_REPLACE);
if (msg.num_elements == 1) {
free(msg.elements);
} else {
ltdb_search_dn1_free(ldb, &msg);
}
return ret;
}
/*
add the index entries for a new record
return -1 on failure
*/
int ltdb_index_add(struct ldb_context *ldb, const struct ldb_message *msg)
{
int ret, i;
struct ldb_message index_list;
/* find the list of indexed fields */
ret = ltdb_search_dn1(ldb, "@INDEXLIST", &index_list);
if (ret != 1) {
/* no indexed fields or an error */
return ret;
}
for (i=0;i<msg->num_elements;i++) {
ret = ldb_msg_find_idx(&index_list, msg->elements[i].name);
if (ret == -1) {
continue;
}
ret = ltdb_index_add1(ldb, msg->dn, &msg->elements[i]);
if (ret == -1) {
ltdb_search_dn1_free(ldb, &index_list);
return -1;
}
}
return 0;
}
/*
delete an index entry for one message element
*/
static int ltdb_index_del1(struct ldb_context *ldb, const char *dn,
struct ldb_message_element *el)
{
struct ldb_message msg;
char *dn_key;
int ret, i;
dn_key = ldb_dn_key(el->name, &el->value);
if (!dn_key) {
return -1;
}
ret = ltdb_search_dn1(ldb, dn_key, &msg);
if (ret == -1) {
free(dn_key);
return -1;
}
if (ret == 0) {
/* it wasn't indexed. Did we have an earlier error? If we did then
its gone now */
ltdb_search_dn1_free(ldb, &msg);
return 0;
}
i = ldb_msg_find_idx(&msg, dn);
if (i == -1) {
/* it ain't there. hmmm */
ltdb_search_dn1_free(ldb, &msg);
return 0;
}
if (i != msg.num_elements - 1) {
memmove(&msg.elements[i], &msg.elements[i+1], sizeof(msg.elements[i]));
}
msg.num_elements--;
if (msg.num_elements == 0) {
ret = ltdb_delete_noindex(ldb, dn_key);
} else {
ret = ltdb_store(ldb, &msg, TDB_REPLACE);
}
ltdb_search_dn1_free(ldb, &msg);
return ret;
}
/*
delete the index entries for a record
return -1 on failure
*/
int ltdb_index_del(struct ldb_context *ldb, const struct ldb_message *msg)
{
int ret, i;
struct ldb_message index_list;
/* find the list of indexed fields */
ret = ltdb_search_dn1(ldb, "@INDEXLIST", &index_list);
if (ret != 1) {
/* no indexed fields or an error */
return ret;
}
for (i=0;i<msg->num_elements;i++) {
ret = ldb_msg_find_idx(&index_list, msg->elements[i].name);
if (ret == -1) {
continue;
}
ret = ltdb_index_del1(ldb, msg->dn, &msg->elements[i]);
if (ret == -1) {
ltdb_search_dn1_free(ldb, &index_list);
return -1;
}
}
return 0;
}

View File

@ -0,0 +1,366 @@
/*
Unix SMB/CIFS implementation.
ldif utilities for ldb
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
/*
this base64 decoder was taken from jitterbug (written by tridge).
we might need to replace it with a new version
*/
static int base64_decode(char *s)
{
const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int bit_offset, byte_offset, idx, i, n;
unsigned char *d = (unsigned char *)s;
char *p;
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 (*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(const char *buf, int len)
{
const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int bit_offset, byte_offset, idx, i;
unsigned char *d = (unsigned char *)buf;
int bytes = (len*8 + 5)/6;
char *out;
out = malloc(bytes+2);
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];
}
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)
{
int i;
unsigned char *p = val->data;
if (val->length == 0 || p[0] == ' ' || p[0] == ':') {
return 1;
}
for (i=0; i<val->length; i++) {
if (!isprint(p[i]) || p[i] == '\n') {
return 1;
}
}
return 0;
}
/*
encode as base64 to a file
*/
static int base64_encode_f(FILE *f, const char *buf, int len, int start_pos)
{
int i;
char *b = ldb_base64_encode(buf, len);
if (!b) {
return -1;
}
for (i=0;b[i];i++) {
fputc(b[i], f);
if (b[i+1] && (i + start_pos) % 77 == 0) {
fputc('\n', f);
fputc(' ', f);
}
}
free(b);
return 0;
}
/*
write a line folded string onto a file
*/
static void fold_string(FILE *f, const char *buf, size_t length, int start_pos)
{
int i;
for (i=0;i<length;i++) {
fputc(buf[i], f);
if (i != (length-1) && (i + start_pos) % 77 == 0) {
fputc('\n', f);
fputc(' ', f);
}
}
}
/*
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(FILE *f)
{
size_t alloc_size=0, chunk_size = 0;
char *chunk = NULL;
int c;
int in_comment = 0;
while ((c = fgetc(f)) != EOF) {
if (chunk_size == alloc_size) {
char *c2;
alloc_size += 1024;
c2 = realloc_p(chunk, char, alloc_size);
if (!c2) {
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;
}
chunk[chunk_size++] = c;
}
return chunk;
}
/* simple ldif attribute parser */
static int next_attr(char **s, char **attr, struct ldb_val *value)
{
char *p;
int base64_encoded = 0;
p = strchr(*s, ':');
if (!p) {
return -1;
}
*p++ = 0;
if (*p == ':') {
base64_encoded = 1;
p++;
}
*attr = *s;
while (isspace(*p)) {
p++;
}
value->data = 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 = base64_decode(value->data);
if (len == -1) {
/* it wasn't valid base64 data */
return -1;
}
value->length = len;
}
return 0;
}
/*
free a message from a ldif_read
*/
void ldif_read_free(struct ldb_message *msg)
{
if (msg->elements) free(msg->elements);
if (msg->private) free(msg->private);
free(msg);
}
/*
read from a LDIF file, creating a ldb_message
*/
struct ldb_message *ldif_read(FILE *f)
{
struct ldb_message *msg;
char *attr=NULL, *chunk=NULL, *s;
struct ldb_val value;
value.data = NULL;
msg = malloc_p(struct ldb_message);
if (!msg) return NULL;
msg->dn = NULL;
msg->elements = NULL;
msg->num_elements = 0;
msg->private = NULL;
chunk = next_chunk(f);
if (!chunk) {
goto failed;
}
msg->private = chunk;
s = chunk;
if (next_attr(&s, &attr, &value) != 0) {
goto failed;
}
/* first line must be a dn */
if (strcmp(attr, "dn") != 0) {
fprintf(stderr, "First line must be a dn not '%s'\n", attr);
goto failed;
}
msg->dn = value.data;
while (next_attr(&s, &attr, &value) == 0) {
msg->elements = realloc_p(msg->elements,
struct ldb_message_element,
msg->num_elements+1);
if (!msg->elements) {
goto failed;
}
msg->elements[msg->num_elements].flags = 0;
msg->elements[msg->num_elements].name = attr;
msg->elements[msg->num_elements].value = value;
msg->num_elements++;
}
return msg;
failed:
if (msg) ldif_read_free(msg);
return NULL;
}
/*
write to a ldif file
*/
void ldif_write(FILE *f, const struct ldb_message *msg)
{
int i;
fprintf(f, "dn: %s\n", msg->dn);
for (i=0;i<msg->num_elements;i++) {
if (ldb_should_b64_encode(&msg->elements[i].value)) {
fprintf(f, "%s:: ", msg->elements[i].name);
base64_encode_f(f,
msg->elements[i].value.data,
msg->elements[i].value.length,
strlen(msg->elements[i].name)+3);
fprintf(f, "\n");
} else {
fprintf(f, "%s: ", msg->elements[i].name);
fold_string(f, msg->elements[i].value.data,
msg->elements[i].value.length,
strlen(msg->elements[i].name)+2);
fprintf(f, "\n");
}
}
fprintf(f,"\n");
}

View File

@ -0,0 +1,176 @@
/*
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 matching
*
* Description: ldb expression matching for tdb backend
*
* Author: Andrew Tridgell
*/
#include "includes.h"
/*
see if two ldb_val structures contain the same data
return 1 for a match, 0 for a mis-match
*/
static int ldb_val_equal(struct ldb_val *v1, 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;
}
/*
check if the scope matches in a search result
*/
static int scope_match(const char *dn, const char *base, enum ldb_scope scope)
{
size_t dn_len, base_len;
if (base == NULL) {
return 1;
}
base_len = strlen(base);
dn_len = strlen(dn);
if (strcmp(dn, base) == 0) {
return 1;
}
if (base_len+1 >= dn_len) {
return 0;
}
switch (scope) {
case LDB_SCOPE_BASE:
break;
case LDB_SCOPE_ONELEVEL:
if (strcmp(dn + (dn_len - base_len), base) == 0 &&
dn[dn_len - base_len - 1] == ',' &&
strchr(dn, ',') == &dn[dn_len - base_len - 1]) {
return 1;
}
break;
case LDB_SCOPE_SUBTREE:
default:
if (strcmp(dn + (dn_len - base_len), base) == 0 &&
dn[dn_len - base_len - 1] == ',') {
return 1;
}
break;
}
return 0;
}
/*
match a leaf node
*/
static int match_leaf(struct ldb_context *ldb,
struct ldb_message *msg,
struct ldb_parse_tree *tree,
const char *base,
enum ldb_scope scope)
{
int i;
if (!scope_match(msg->dn, base, scope)) {
return 0;
}
if (strcmp(tree->u.simple.attr, "dn") == 0) {
if (strcmp(tree->u.simple.value.data, "*") == 0) {
return 1;
}
return strcmp(msg->dn, tree->u.simple.value.data) == 0;
}
for (i=0;i<msg->num_elements;i++) {
if (strcmp(msg->elements[i].name, tree->u.simple.attr) == 0 &&
(strcmp(tree->u.simple.value.data, "*") == 0 ||
ldb_val_equal(&msg->elements[i].value, &tree->u.simple.value))) {
return 1;
}
}
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
*/
int ldb_message_match(struct ldb_context *ldb,
struct ldb_message *msg,
struct ldb_parse_tree *tree,
const char *base,
enum ldb_scope scope)
{
int v, i;
switch (tree->operation) {
case LDB_OP_SIMPLE:
break;
case LDB_OP_NOT:
return ! ldb_message_match(ldb, msg, tree->u.not.child, base, scope);
case LDB_OP_AND:
for (i=0;i<tree->u.list.num_elements;i++) {
v = ldb_message_match(ldb, msg, tree->u.list.elements[i],
base, scope);
if (!v) return 0;
}
return 1;
case LDB_OP_OR:
for (i=0;i<tree->u.list.num_elements;i++) {
v = ldb_message_match(ldb, msg, tree->u.list.elements[i],
base, scope);
if (v) return 1;
}
return 0;
}
return match_leaf(ldb, msg, tree, base, scope);
}

View File

@ -0,0 +1,174 @@
/*
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 pack/unpack
*
* Description: pack/unpack routines for ldb messages as key/value blobs
*
* Author: Andrew Tridgell
*/
#include "includes.h"
#include "ldb_tdb/ldb_tdb.h"
/* change this if the data format ever changes */
#define LTDB_PACKING_FORMAT 0x26011966
/*
pack a ldb message into a linear buffer in a TDB_DATA
caller frees the data buffer after use
*/
int ltdb_pack_data(struct ldb_context *ctx,
const struct ldb_message *message,
struct TDB_DATA *data)
{
int i;
size_t size;
char *p;
/* work out how big it needs to be */
size = 8;
for (i=0;i<message->num_elements;i++) {
size += 1 + strlen(message->elements[i].name);
size += 4 + message->elements[i].value.length + 1;
}
/* allocate it */
data->dptr = malloc(size);
if (!data->dptr) {
errno = ENOMEM;
return -1;
}
data->dsize = size;
p = data->dptr;
SIVAL(p, 0, LTDB_PACKING_FORMAT);
SIVAL(p, 4, message->num_elements);
p += 8;
for (i=0;i<message->num_elements;i++) {
size_t len = strlen(message->elements[i].name);
memcpy(p, message->elements[i].name, len+1);
p += len + 1;
SIVAL(p, 0, message->elements[i].value.length);
memcpy(p+4, message->elements[i].value.data,
message->elements[i].value.length);
p[4+message->elements[i].value.length] = 0;
p += 4 + message->elements[i].value.length + 1;
}
return 0;
}
/*
unpack a ldb message from a linear buffer in TDB_DATA
note that this does not fill in the class and key elements
caller frees. Memory for the elements[] array is malloced,
but the memory for the elements is re-used from the TDB_DATA
data. This means the caller only has to free the elements array
*/
int ltdb_unpack_data(struct ldb_context *ctx,
const struct TDB_DATA *data,
struct ldb_message *message)
{
char *p;
unsigned int remaining;
int i;
message->elements = NULL;
p = data->dptr;
if (data->dsize < 4) {
errno = EIO;
goto failed;
}
if (IVAL(p, 0) != LTDB_PACKING_FORMAT) {
/* this is where we will cope with upgrading the
format if/when the format is ever changed */
errno = EIO;
goto failed;
}
message->num_elements = IVAL(p, 4);
p += 8;
if (message->num_elements == 0) {
message->elements = NULL;
return 0;
}
/* basic sanity check */
remaining = data->dsize - 8;
if (message->num_elements > remaining / 6) {
errno = EIO;
goto failed;
}
message->elements = malloc_array_p(struct ldb_message_element,
message->num_elements);
if (!message->elements) {
errno = ENOMEM;
goto failed;
}
for (i=0;i<message->num_elements;i++) {
size_t len;
if (remaining < 6) {
errno = EIO;
goto failed;
}
len = strnlen(p, remaining-6);
message->elements[i].name = p;
remaining -= len + 1;
p += len + 1;
len = IVAL(p, 0);
if (len > remaining-5) {
errno = EIO;
goto failed;
}
message->elements[i].value.length = len;
message->elements[i].value.data = p+4;
remaining -= len+4+1;
p += len+4+1;
}
return 0;
failed:
if (message->elements) {
free(message->elements);
}
return -1;
}

View File

@ -0,0 +1,448 @@
/*
Unix SMB/CIFS implementation.
parse a LDAP-like expression
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
TODO:
- add RFC2254 binary string handling
- possibly add ~=, <= and >= handling
- expand the test suite
- add better parse error handling
*/
#include "includes.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> ::= '=' | '~=' | '<=' | '>='
*/
/*
return next token element. Caller frees
*/
static char *ldb_parse_lex(const char **s)
{
const char *p = *s;
char *ret;
while (isspace(*p)) {
p++;
}
*s = p;
if (*p == 0) {
return NULL;
}
if (strchr("()&|=!", *p)) {
(*s) = p+1;
ret = strndup(p, 1);
if (!ret) {
errno = ENOMEM;
}
return ret;
}
while (*p && (isalnum(*p) || !strchr("()&|=!", *p))) {
p++;
}
if (p == *s) {
return NULL;
}
ret = strndup(*s, p - *s);
if (!ret) {
errno = ENOMEM;
}
*s = p;
return ret;
}
/*
find a matching close brace in a string
*/
static const char *match_brace(const char *s)
{
unsigned int count = 0;
while (*s && (count != 0 || *s != ')')) {
if (*s == '(') {
count++;
}
if (*s == ')') {
count--;
}
s++;
}
if (! *s) {
return NULL;
}
return s;
}
static struct ldb_parse_tree *ldb_parse_filter(const char **s);
/*
<simple> ::= <attributetype> <filtertype> <attributevalue>
*/
static struct ldb_parse_tree *ldb_parse_simple(const char *s)
{
char *eq, *val, *l;
struct ldb_parse_tree *ret;
l = ldb_parse_lex(&s);
if (!l) {
fprintf(stderr, "Unexpected end of expression\n");
return NULL;
}
if (strchr("()&|=", *l)) {
fprintf(stderr, "Unexpected token '%s'\n", l);
free(l);
return NULL;
}
eq = ldb_parse_lex(&s);
if (!eq || strcmp(eq, "=") != 0) {
fprintf(stderr, "Expected '='\n");
free(l);
if (eq) free(eq);
return NULL;
}
free(eq);
val = ldb_parse_lex(&s);
if (val && strchr("()&|=", *val)) {
fprintf(stderr, "Unexpected token '%s'\n", val);
free(l);
if (val) free(val);
return NULL;
}
ret = malloc_p(struct ldb_parse_tree);
if (!ret) {
errno = ENOMEM;
return NULL;
}
ret->operation = LDB_OP_SIMPLE;
ret->u.simple.attr = l;
ret->u.simple.value.data = val;
ret->u.simple.value.length = val?strlen(val):0;
return ret;
}
/*
parse a filterlist
<and> ::= '&' <filterlist>
<or> ::= '|' <filterlist>
<filterlist> ::= <filter> | <filter> <filterlist>
*/
static struct ldb_parse_tree *ldb_parse_filterlist(enum ldb_parse_op op, const char *s)
{
struct ldb_parse_tree *ret, *next;
ret = malloc_p(struct ldb_parse_tree);
if (!ret) {
errno = ENOMEM;
return NULL;
}
ret->operation = op;
ret->u.list.num_elements = 1;
ret->u.list.elements = malloc_p(struct ldb_parse_tree *);
if (!ret->u.list.elements) {
errno = ENOMEM;
free(ret);
return NULL;
}
ret->u.list.elements[0] = ldb_parse_filter(&s);
if (!ret->u.list.elements[0]) {
free(ret->u.list.elements);
free(ret);
return NULL;
}
while (isspace(*s)) s++;
while (*s && (next = ldb_parse_filter(&s))) {
struct ldb_parse_tree **e;
e = realloc_p(ret->u.list.elements,
struct ldb_parse_tree *,
ret->u.list.num_elements+1);
if (!e) {
errno = ENOMEM;
ldb_parse_tree_free(next);
ldb_parse_tree_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(*s)) s++;
}
return ret;
}
/*
<not> ::= '!' <filter>
*/
static struct ldb_parse_tree *ldb_parse_not(const char *s)
{
struct ldb_parse_tree *ret;
ret = malloc_p(struct ldb_parse_tree);
if (!ret) {
errno = ENOMEM;
return NULL;
}
ret->operation = LDB_OP_NOT;
ret->u.not.child = ldb_parse_filter(&s);
if (!ret->u.not.child) {
free(ret);
return NULL;
}
return ret;
}
/*
parse a filtercomp
<filtercomp> ::= <and> | <or> | <not> | <simple>
*/
static struct ldb_parse_tree *ldb_parse_filtercomp(const char *s)
{
while (isspace(*s)) s++;
switch (*s) {
case '&':
return ldb_parse_filterlist(LDB_OP_AND, s+1);
case '|':
return ldb_parse_filterlist(LDB_OP_OR, s+1);
case '!':
return ldb_parse_not(s+1);
case '(':
case ')':
fprintf(stderr, "Unexpected token '%c'\n", *s);
return NULL;
}
return ldb_parse_simple(s);
}
/*
<filter> ::= '(' <filtercomp> ')'
*/
static struct ldb_parse_tree *ldb_parse_filter(const char **s)
{
char *l, *s2;
const char *p, *p2;
struct ldb_parse_tree *ret;
l = ldb_parse_lex(s);
if (!l) {
fprintf(stderr, "Unexpected end of expression\n");
return NULL;
}
if (strcmp(l, "(") != 0) {
free(l);
fprintf(stderr, "Expected '('\n");
return NULL;
}
free(l);
p = match_brace(*s);
if (!p) {
fprintf(stderr, "Parse error - mismatched braces\n");
return NULL;
}
p2 = p + 1;
s2 = strndup(*s, p - *s);
if (!s2) {
errno = ENOMEM;
return NULL;
}
ret = ldb_parse_filtercomp(s2);
free(s2);
*s = p2;
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(const char *s)
{
while (isspace(*s)) s++;
if (*s == '(') {
return ldb_parse_filter(&s);
}
return ldb_parse_simple(s);
}
/*
free a parse tree returned from ldb_parse_tree()
*/
void ldb_parse_tree_free(struct ldb_parse_tree *tree)
{
int i;
switch (tree->operation) {
case LDB_OP_SIMPLE:
free(tree->u.simple.attr);
if (tree->u.simple.value.data) free(tree->u.simple.value.data);
break;
case LDB_OP_AND:
case LDB_OP_OR:
for (i=0;i<tree->u.list.num_elements;i++) {
ldb_parse_tree_free(tree->u.list.elements[i]);
}
if (tree->u.list.elements) free(tree->u.list.elements);
break;
case LDB_OP_NOT:
ldb_parse_tree_free(tree->u.not.child);
break;
}
free(tree);
}
#if TEST_PROGRAM
/*
return a string representation of a parse tree
used for debugging
*/
static char *tree_string(struct ldb_parse_tree *tree)
{
char *s = NULL;
char *s1, *s2;
int i;
switch (tree->operation) {
case LDB_OP_SIMPLE:
asprintf(&s, "( %s = \"%s\" )", tree->u.simple.attr,
(char *)tree->u.simple.value.data);
break;
case LDB_OP_AND:
case LDB_OP_OR:
asprintf(&s, "( %c", tree->operation==LDB_OP_AND?'&':'|');
if (!s) return NULL;
for (i=0;i<tree->u.list.num_elements;i++) {
s1 = tree_string(tree->u.list.elements[i]);
if (!s1) {
free(s);
return NULL;
}
asprintf(&s2, "%s %s", s, s1);
free(s);
free(s1);
s = s2;
}
if (!s) {
return NULL;
}
asprintf(&s2, "%s )", s);
free(s);
s = s2;
break;
case LDB_OP_NOT:
s1 = tree_string(tree->u.not.child);
asprintf(&s, "( ! %s )", s1);
free(s1);
break;
}
return s;
}
/*
print a tree
*/
static void print_tree(struct ldb_parse_tree *tree)
{
char *s = tree_string(tree);
printf("%s\n", s);
free(s);
}
int main(void)
{
char line[1000];
int ret = 0;
while (fgets(line, sizeof(line)-1, stdin)) {
struct ldb_parse_tree *tree;
if (line[strlen(line)-1] == '\n') {
line[strlen(line)-1] = 0;
}
tree = ldb_parse_tree(line);
if (!tree) {
fprintf(stderr, "Failed to parse\n");
ret = 1;
continue;
}
print_tree(tree);
ldb_parse_tree_free(tree);
}
return ret;
}
#endif /* TEST_PROGRAM */

View File

@ -0,0 +1,40 @@
/*
Unix SMB/CIFS implementation.
parse a LDAP-like expression - header
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
enum ldb_parse_op {LDB_OP_SIMPLE, LDB_OP_AND, LDB_OP_OR, LDB_OP_NOT};
struct ldb_parse_tree {
enum ldb_parse_op operation;
union {
struct {
char *attr;
struct ldb_val value;
} simple;
struct {
unsigned int num_elements;
struct ldb_parse_tree **elements;
} list;
struct {
struct ldb_parse_tree *child;
} not;
} u;
};

View File

@ -0,0 +1,482 @@
/*
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 search functions
*
* Description: functions to search ldb+tdb databases
*
* Author: Andrew Tridgell
*/
#include "includes.h"
#include "ldb_tdb/ldb_tdb.h"
/*
free a message that has all parts separately allocated
*/
static void msg_free_all_parts(struct ldb_message *msg)
{
int i;
if (msg->dn) free(msg->dn);
for (i=0;i<msg->num_elements;i++) {
if (msg->elements[i].name) free(msg->elements[i].name);
if (msg->elements[i].value.data) free(msg->elements[i].value.data);
}
free(msg->elements);
free(msg);
}
/*
TODO: this should take advantage of the sorted nature of the message
return index of the attribute, or -1 if not found
*/
int ldb_msg_find_attr(const struct ldb_message *msg, const char *attr)
{
int i;
for (i=0;i<msg->num_elements;i++) {
if (strcmp(msg->elements[i].name, attr) == 0) {
return i;
}
}
return -1;
}
/*
duplicate a ldb_val structure
*/
static struct ldb_val ldb_val_dup(const struct ldb_val *v)
{
struct ldb_val v2;
v2.length = v->length;
if (v->length == 0) {
v2.data = NULL;
return v2;
}
/* the +1 is to cope with buggy C library routines like strndup
that look one byte beyond */
v2.data = malloc(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 one element to a message
*/
static int msg_add_element(struct ldb_message *ret, const struct ldb_message_element *el)
{
struct ldb_message_element *e2;
e2 = realloc_p(ret->elements, struct ldb_message_element, ret->num_elements+1);
if (!e2) {
return -1;
}
ret->elements = e2;
e2[ret->num_elements].name = strdup(el->name);
if (!e2[ret->num_elements].name) {
return -1;
}
e2[ret->num_elements].value = ldb_val_dup(&el->value);
if (e2[ret->num_elements].value.length != el->value.length) {
return -1;
}
ret->num_elements++;
return 0;
}
/*
add all elements from one message into another
*/
static int msg_add_all_elements(struct ldb_message *ret,
const struct ldb_message *msg)
{
int i;
for (i=0;i<msg->num_elements;i++) {
if (msg_add_element(ret, &msg->elements[i]) != 0) {
return -1;
}
}
return 0;
}
/*
pull the specified list of attributes from a message
*/
static struct ldb_message *ltdb_pull_attrs(struct ldb_context *ldb,
const struct ldb_message *msg,
const char **attrs)
{
struct ldb_message *ret;
int i;
ret = malloc_p(struct ldb_message);
if (!ret) {
return NULL;
}
ret->dn = strdup(msg->dn);
if (!ret->dn) {
free(ret);
return NULL;
}
ret->num_elements = 0;
ret->elements = NULL;
ret->private = NULL;
if (!attrs) {
if (msg_add_all_elements(ret, msg) != 0) {
msg_free_all_parts(ret);
return NULL;
}
return ret;
}
for (i=0;attrs[i];i++) {
int j;
if (strcmp(attrs[i], "*") == 0) {
if (msg_add_all_elements(ret, msg) != 0) {
msg_free_all_parts(ret);
return NULL;
}
continue;
}
j = ldb_msg_find_attr(msg, attrs[i]);
if (j == -1) {
continue;
}
do {
if (msg_add_element(ret, &msg->elements[j]) != 0) {
msg_free_all_parts(ret);
return NULL;
}
} while (++j < msg->num_elements &&
strcmp(attrs[i], msg->elements[j].name) == 0);
}
return ret;
}
/*
see if a ldb_val is a wildcard
*/
int ltdb_has_wildcard(const struct ldb_val *val)
{
if (val->length == 1 && ((char *)val->data)[0] == '*') {
return 1;
}
return 0;
}
/*
free the results of a ltdb_search_dn1 search
*/
void ltdb_search_dn1_free(struct ldb_context *ldb, struct ldb_message *msg)
{
free(msg->dn);
free(msg->private);
if (msg->elements) free(msg->elements);
}
/*
search the database for a single simple dn, returning all attributes
in a single message
return 1 on success, 0 on record-not-found and -1 on error
*/
int ltdb_search_dn1(struct ldb_context *ldb, const char *dn, struct ldb_message *msg)
{
struct ltdb_private *ltdb = ldb->private;
int ret;
TDB_DATA tdb_key, tdb_data;
/* form the key */
tdb_key = ltdb_key(dn);
if (!tdb_key.dptr) {
return -1;
}
tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
free(tdb_key.dptr);
if (!tdb_data.dptr) {
return 0;
}
msg->dn = strdup(dn);
if (!msg->dn) {
free(tdb_data.dptr);
return -1;
}
msg->private = tdb_data.dptr;
msg->num_elements = 0;
msg->elements = NULL;
ret = ltdb_unpack_data(ldb, &tdb_data, msg);
if (ret == -1) {
free(tdb_data.dptr);
return -1;
}
return 1;
}
/*
search the database for a single simple dn
*/
int ltdb_search_dn(struct ldb_context *ldb, char *dn,
const char *attrs[], struct ldb_message ***res)
{
int ret;
struct ldb_message msg, *msg2;
ret = ltdb_search_dn1(ldb, dn, &msg);
if (ret != 1) {
return ret;
}
msg2 = ltdb_pull_attrs(ldb, &msg, attrs);
ltdb_search_dn1_free(ldb, &msg);
if (!msg2) {
return -1;
}
*res = malloc_array_p(struct ldb_message *, 2);
if (! *res) {
msg_free_all_parts(msg2);
return -1;
}
(*res)[0] = msg2;
(*res)[1] = NULL;
return 1;
}
/*
add a set of attributes from a record to a set of results
return 0 on success, -1 on failure
*/
int ltdb_add_attr_results(struct ldb_context *ldb, struct ldb_message *msg,
const char *attrs[],
unsigned int *count,
struct ldb_message ***res)
{
struct ldb_message *msg2;
struct ldb_message **res2;
/* pull the attributes that the user wants */
msg2 = ltdb_pull_attrs(ldb, msg, attrs);
if (!msg2) {
return -1;
}
/* add to the results list */
res2 = realloc_p(*res, struct ldb_message *, (*count)+2);
if (!res2) {
msg_free_all_parts(msg2);
return -1;
}
(*res) = res2;
(*res)[*count] = msg2;
(*res)[(*count)+1] = NULL;
(*count)++;
return 0;
}
/*
internal search state during a full db search
*/
struct ltdb_search_info {
struct ldb_context *ldb;
struct ldb_parse_tree *tree;
const char *base;
enum ldb_scope scope;
const char **attrs;
struct ldb_message **msgs;
int failures;
int count;
};
/*
search function for a non-indexed search
*/
static int search_func(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state)
{
struct ltdb_search_info *sinfo = state;
struct ldb_message msg;
int ret;
if (key.dsize < 4 ||
strncmp(key.dptr, "DN=", 3) != 0) {
return 0;
}
msg.dn = key.dptr + 3;
/* unpack the record */
ret = ltdb_unpack_data(sinfo->ldb, &data, &msg);
if (ret == -1) {
sinfo->failures++;
return 0;
}
/* see if it matches the given expression */
if (!ldb_message_match(sinfo->ldb, &msg, sinfo->tree,
sinfo->base, sinfo->scope)) {
if (msg.elements) free(msg.elements);
return 0;
}
ret = ltdb_add_attr_results(sinfo->ldb, &msg, sinfo->attrs, &sinfo->count, &sinfo->msgs);
if (ret == -1) {
sinfo->failures++;
}
if (msg.elements) free(msg.elements);
return ret;
}
/*
free a set of search results
*/
int ltdb_search_free(struct ldb_context *ldb, struct ldb_message **msgs)
{
int i;
if (!msgs) return 0;
for (i=0;msgs[i];i++) {
msg_free_all_parts(msgs[i]);
}
free(msgs);
return 0;
}
/*
search the database with a LDAP-like expression.
this is the "full search" non-indexed varient
*/
static int ltdb_search_full(struct ldb_context *ldb,
const char *base,
enum ldb_scope scope,
struct ldb_parse_tree *tree,
const char *attrs[], struct ldb_message ***res)
{
struct ltdb_private *ltdb = ldb->private;
int ret;
struct ltdb_search_info sinfo;
sinfo.tree = tree;
sinfo.ldb = ldb;
sinfo.scope = scope;
sinfo.base = base;
sinfo.attrs = attrs;
sinfo.msgs = NULL;
sinfo.count = 0;
sinfo.failures = 0;
ret = tdb_traverse(ltdb->tdb, search_func, &sinfo);
if (ret == -1) {
ltdb_search_free(ldb, sinfo.msgs);
return -1;
}
*res = sinfo.msgs;
return sinfo.count;
}
/*
search the database with a LDAP-like expression.
choses a search method
*/
int ltdb_search(struct ldb_context *ldb, const char *base,
enum ldb_scope scope, const char *expression,
const char *attrs[], struct ldb_message ***res)
{
struct ldb_parse_tree *tree;
int ret;
*res = NULL;
/* form a parse tree for the expression */
tree = ldb_parse_tree(expression);
if (!tree) {
return -1;
}
if (tree->operation == LDB_OP_SIMPLE &&
strcmp(tree->u.simple.attr, "dn") == 0 &&
!ltdb_has_wildcard(&tree->u.simple.value)) {
/* yay! its a nice simple one */
ret = ltdb_search_dn(ldb, tree->u.simple.value.data, attrs, res);
} else {
ret = ltdb_search_indexed(ldb, base, scope, tree, attrs, res);
if (ret == -1) {
ret = ltdb_search_full(ldb, base, scope, tree, attrs, res);
}
}
ldb_parse_tree_free(tree);
return ret;
}

View File

@ -0,0 +1,303 @@
/*
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 tdb backend
*
* Description: core functions for tdb backend
*
* Author: Andrew Tridgell
*/
#include "includes.h"
#include "ldb_tdb/ldb_tdb.h"
/*
form a TDB_DATA for a record key
caller frees
*/
struct TDB_DATA ltdb_key(const char *dn)
{
TDB_DATA key;
char *key_str = NULL;
asprintf(&key_str, "DN=%s", dn);
if (!key_str) {
errno = ENOMEM;
key.dptr = NULL;
key.dsize = 0;
return key;
}
key.dptr = key_str;
key.dsize = strlen(key_str)+1;
return key;
}
/*
store a record into the db
*/
int ltdb_store(struct ldb_context *ldb, const struct ldb_message *msg, int flgs)
{
struct ltdb_private *ltdb = ldb->private;
TDB_DATA tdb_key, tdb_data;
int ret;
tdb_key = ltdb_key(msg->dn);
if (!tdb_key.dptr) {
return -1;
}
ret = ltdb_pack_data(ldb, msg, &tdb_data);
if (ret == -1) {
free(tdb_key.dptr);
return -1;
}
ret = tdb_store(ltdb->tdb, tdb_key, tdb_data, flgs);
if (ret == -1) {
goto done;
}
ret = ltdb_index_add(ldb, msg);
if (ret == -1) {
tdb_delete(ltdb->tdb, tdb_key);
}
done:
free(tdb_key.dptr);
free(tdb_data.dptr);
return ret;
}
/*
add a record to the database
*/
static int ltdb_add(struct ldb_context *ldb, const struct ldb_message *msg)
{
return ltdb_store(ldb, msg, TDB_INSERT);
}
/*
delete a record from the database, not updating indexes (used for deleting
index records)
*/
int ltdb_delete_noindex(struct ldb_context *ldb, const char *dn)
{
struct ltdb_private *ltdb = ldb->private;
TDB_DATA tdb_key;
int ret;
tdb_key = ltdb_key(dn);
if (!tdb_key.dptr) {
return -1;
}
ret = tdb_delete(ltdb->tdb, tdb_key);
free(tdb_key.dptr);
return ret;
}
/*
delete a record from the database
*/
static int ltdb_delete(struct ldb_context *ldb, const char *dn)
{
int ret;
struct ldb_message msg;
/* in case any attribute of the message was indexed, we need
to fetch the old record */
ret = ltdb_search_dn1(ldb, dn, &msg);
if (ret != 1) {
/* not finding the old record is an error */
return -1;
}
ret = ltdb_delete_noindex(ldb, dn);
if (ret == -1) {
ltdb_search_dn1_free(ldb, &msg);
return -1;
}
/* remove any indexed attributes */
ret = ltdb_index_del(ldb, &msg);
ltdb_search_dn1_free(ldb, &msg);
return ret;
}
/*
modify a record
*/
static int ltdb_modify(struct ldb_context *ldb, const struct ldb_message *msg)
{
struct ltdb_private *ltdb = ldb->private;
TDB_DATA tdb_key, tdb_data;
struct ldb_message msg2;
int ret;
tdb_key = ltdb_key(msg->dn);
if (!tdb_key.dptr) {
return -1;
}
tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
if (!tdb_data.dptr) {
free(tdb_key.dptr);
return -1;
}
ret = ltdb_unpack_data(ldb, &tdb_data, &msg2);
if (ret == -1) {
free(tdb_key.dptr);
free(tdb_data.dptr);
return -1;
}
#if 0
for (i=0;i<msg->num_elements;i++) {
switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) {
case LDB_FLAG_MOD_ADD:
ret = find_element(&msg2, msg->elements[i].name);
if (ret != -1) {
errno = EEXIST;
goto failed;
}
}
}
failed:
#endif
free(tdb_key.dptr);
free(tdb_data.dptr);
if (msg2.elements) free(msg2.elements);
return -1;
}
/*
close database
*/
static int ltdb_close(struct ldb_context *ldb)
{
struct ltdb_private *ltdb = ldb->private;
int ret;
ret = tdb_close(ltdb->tdb);
free(ltdb);
free(ldb);
return ret;
}
/*
return extended error information
*/
static const char *ltdb_errstring(struct ldb_context *ldb)
{
struct ltdb_private *ltdb = ldb->private;
return tdb_errorstr(ltdb->tdb);
}
static const struct ldb_backend_ops ltdb_ops = {
ltdb_close,
ltdb_search,
ltdb_search_free,
ltdb_add,
ltdb_modify,
ltdb_delete,
ltdb_errstring
};
/*
connect to the database
*/
struct ldb_context *ltdb_connect(const char *url,
unsigned int flags,
const char *options[])
{
const char *path;
int tdb_flags, open_flags;
struct ltdb_private *ltdb;
TDB_CONTEXT *tdb;
struct ldb_context *ldb;
/* parse the url */
if (strncmp(url, "tdb://", 6) != 0) {
errno = EINVAL;
return NULL;
}
path = url+6;
tdb_flags = TDB_DEFAULT;
if (flags & LDB_FLG_RDONLY) {
open_flags = O_RDONLY;
} else {
open_flags = O_CREAT | O_RDWR;
}
tdb = tdb_open(path, 0, tdb_flags, open_flags, 0666);
if (!tdb) {
return NULL;
}
ltdb = malloc_p(struct ltdb_private);
if (!ltdb) {
tdb_close(tdb);
errno = ENOMEM;
return NULL;
}
ltdb->tdb = tdb;
ldb = malloc_p(struct ldb_context);
if (!ldb) {
tdb_close(tdb);
free(ltdb);
errno = ENOMEM;
return NULL;
}
ldb->private = ltdb;
ldb->ops = &ltdb_ops;
return ldb;
}

View File

@ -0,0 +1,11 @@
/* this private structure is used by the ltdb backend in the
ldb_context */
struct ltdb_private {
TDB_CONTEXT *tdb;
unsigned int connect_flags;
};
#define IVAL(p, ofs) (((unsigned *)((char *)(p) + (ofs)))[0])
#define SIVAL(p, ofs, v) do { IVAL(p, ofs) = (v); } while (0)

View File

@ -0,0 +1,55 @@
/*
Unix SMB/CIFS implementation.
a utility to add elements to a ldb
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
int main(void)
{
static struct ldb_context *ldb;
struct ldb_message *msg;
int ret;
int count=0, failures=0;
ldb = ltdb_connect("tdb://test.ldb", 0, NULL);
if (!ldb) {
perror("ldb_connect");
exit(1);
}
while ((msg = ldif_read(stdin))) {
ret = ldb->ops->add(ldb, msg);
if (ret != 0) {
fprintf(stderr, "Failed to add record '%s'\n", msg->dn);
failures++;
} else {
count++;
}
ldif_read_free(msg);
}
ldb->ops->close(ldb);
printf("Added %d records with %d failures\n", count, failures);
return 0;
}

View File

@ -0,0 +1,50 @@
/*
Unix SMB/CIFS implementation.
a utility to delete elements in a ldb
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
int main(int argc, const char *argv[])
{
static struct ldb_context *ldb;
int ret, i;
if (argc < 2) {
printf("Usage: ldbdel <dn...>\n");
exit(1);
}
ldb = ltdb_connect("tdb://test.ldb", 0, NULL);
if (!ldb) {
perror("ldb_connect");
exit(1);
}
for (i=1;i<argc;i++) {
ret = ldb->ops->delete(ldb, argv[i]);
if (ret != 0) {
printf("delete of '%s' failed\n", argv[i]);
}
}
ldb->ops->close(ldb);
return 0;
}

View File

@ -0,0 +1,73 @@
/*
Unix SMB/CIFS implementation.
simple ldb search tool
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
int main(int argc, const char *argv[])
{
static struct ldb_context *ldb;
struct ldb_message **msgs;
int ret, i;
const char *expression;
const char **attrs = NULL;
if (argc < 2) {
printf("Usage: ldbsearch <expression> [attrs...]\n");
exit(1);
}
if (argc > 2) {
attrs = argv+2;
}
expression = argv[1];
ldb = ltdb_connect("tdb://test.ldb", 0, NULL);
if (!ldb) {
perror("ldb_connect");
exit(1);
}
ret = ldb->ops->search(ldb, expression, attrs, &msgs);
if (ret == -1) {
printf("search failed\n");
exit(1);
}
printf("# returned %d records\n", ret);
for (i=0;i<ret;i++) {
printf("# record %d\n", i+1);
ldif_write(stdout, msgs[i]);
}
ret = ldb->ops->search_free(ldb, msgs);
if (ret == -1) {
fprintf(stderr, "search_free failed\n");
exit(1);
}
ldb->ops->close(ldb);
return 0;
}

View File

@ -0,0 +1,15 @@
dn: o=University of Michigan,c=US
objectclass: organization
objectclass: domainRelatedObject
l: Ann Arbor, Michigan
st: Michigan
o: University of Michigan
o: UMICH
o: UM
o: U-M
o: U of M
description: The University of Michigan at Ann Arbor
postaladdress: University of Michigan $ 535 W. William St. $ Ann Arbor, MI 481
09 $ US
telephonenumber: +1 313 764-1817
associateddomain: example.com

View File

@ -0,0 +1,11 @@
#!/bin/sh
export PATH=/home/tridge/samba/openldap/prefix/sbin:/home/tridge/samba/openldap/prefix/bin:/home/tridge/samba/openldap/prefix/libexec:$PATH
rm -rf tests/tmp/db
mkdir -p tests/tmp/db
killall slapd
sleep 2
killall -9 slapd
slapadd -f tests/slapd.conf < tests/init.ldif || exit 1

View File

@ -0,0 +1,11 @@
#!/bin/sh
# aargh, did LDAP ever have to expose this crap to users ...
BASE=`pwd`
TMPDIR=$BASE/tests/tmp
LDAPI_ESCAPE=`echo $TMPDIR/ldapi | sed 's|/|%2F|g'`
echo "ldapi://$LDAPI_ESCAPE"

View File

@ -0,0 +1,25 @@
loglevel 0
include tests/schema/core.schema
include tests/schema/cosine.schema
include tests/schema/inetorgperson.schema
include tests/schema/openldap.schema
include tests/schema/nis.schema
pidfile tests/tmp/slapd.pid
argsfile tests/tmp/slapd.args
access to * by * write
allow update_anon bind_anon_dn
defaultsearchbase "o=University of Michigan,c=US"
database ldbm
suffix "o=University of Michigan,c=US"
directory tests/tmp/db
index objectClass eq
index drink eq
index title eq

View File

@ -0,0 +1,8 @@
#!/bin/sh
export PATH=/home/tridge/samba/openldap/prefix/sbin:/home/tridge/samba/openldap/prefix/bin:/home/tridge/samba/openldap/prefix/libexec:$PATH
mkdir -p tests/tmp/db
slapd -f tests/slapd.conf -h "`tests/ldapi_url.sh`" $*

View File

@ -0,0 +1,4 @@
dn: @INDEXLIST
@IDXATTR: drink
@IDXATTR: title
@IDXATTR: objectclass

View File

@ -0,0 +1,416 @@
dn: ou=People,o=University of Michigan,c=US
objectclass: organizationalUnit
objectclass: extensibleObject
ou: People
uidNumber: 0
gidNumber: 0
dn: ou=Groups,o=University of Michigan,c=US
objectclass: organizationalUnit
ou: Groups
dn: ou=Information Technology Division,ou=People,o=University of Michigan,c=US
objectclass: organizationalUnit
ou: Information Technology Division
description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
oLDg8KCw4LCgzBBMUFhMUFrMUE=
description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
#LEAD COMMENT
# another comment
dn: CN=All Staff,ou=Groups,o=University of Michigan,c=US
#EMBEDDED COMMENT
member: cn=Manager,o=University of Michigan,c=US
member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Unive
rsity of Michigan,c=US
member: cn=Jane Doe,ou=Alumni Association,ou=People,o=University of Michigan,c
=US
member: cn=John Doe,ou=Information Technology Division,ou=People,o=University
of Michigan,c=US
member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=University of Michiga
n,c=US
member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=University of Mic
higan,c=US
member: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Univ
ersity of Michigan,c=US
member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=University of Mich
igan,c=US
member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=University of Mic
higan,c=US
member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Mic
higan,c=US
member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Univers
ity of Michigan,c=US
owner: cn=Manager,o=University of Michigan,c=US
cn: All Staff
description: Everyone in the sample data
objectclass: groupofnames
dn: cn=Alumni Assoc Staff,ou=Groups,o=University of Michigan,c=US
member: cn=Manager,o=University of Michigan,c=US
member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=University of Mic
higan,c=US
member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=University of Mic
higan,c=US
member: cn=Jane Doe,ou=Alumni Association,ou=People,o=University of Michigan,c
=US
member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=University of Mich
igan,c=US
member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=University of Michiga
n,c=US
member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Mic
higan,c=US
owner: cn=Manager,o=University of Michigan,c=US
description: All Alumni Assoc Staff
cn: Alumni Assoc Staff
objectclass: groupofnames
dn: ou=Alumni Association,ou=People,o=University of Michigan,c=US
objectclass: organizationalUnit
ou: Alumni Association
dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Universit
y of Michigan,c=US
objectclass: OpenLDAPperson
cn: Barbara Jensen
cn: Babs Jensen
sn:: IEplbnNlbiA=
uid: bjensen
title: Mythical Manager, Research Systems
postaladdress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Ann
Arbor, MI 48103-4943
seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=US
userpassword:: YmplbnNlbg==
mail: bjensen@mailgw.example.com
homepostaladdress: 123 Wesley $ Ann Arbor, MI 48103
description: Mythical manager of the rsdd unix project
drink: water
homephone: +1 313 555 2333
pager: +1 313 555 3233
facsimiletelephonenumber: +1 313 555 2274
telephonenumber: +1 313 555 9022
dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=University
of Michigan,c=US
objectclass: OpenLDAPperson
cn: Bjorn Jensen
cn: Biiff Jensen
sn: Jensen
uid: bjorn
seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=US
userpassword:: Ympvcm4=
homepostaladdress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
drink: Iced Tea
description: Hiker, biker
title: Director, Embedded Systems
postaladdress: Info Tech Division $ 535 W. William St. $ Ann Arbor, MI 48103
mail: bjorn@mailgw.example.com
homephone: +1 313 555 5444
pager: +1 313 555 4474
facsimiletelephonenumber: +1 313 555 2177
telephonenumber: +1 313 555 0355
dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=University of Michiga
n,c=US
objectclass: OpenLDAPperson
cn: Dorothy Stevens
cn: Dot Stevens
sn: Stevens
uid: dots
title: Secretary, UM Alumni Association
postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=US
drink: Lemonade
homepostaladdress: 377 White St. Apt. 3 $ Ann Arbor, MI 48104
description: Very tall
facsimiletelephonenumber: +1 313 555 3223
telephonenumber: +1 313 555 3664
mail: dots@mail.alumni.example.com
homephone: +1 313 555 0454
dn: cn=ITD Staff,ou=Groups,o=University of Michigan,c=US
owner: cn=Manager,o=University of Michigan,c=US
description: All ITD Staff
cn: ITD Staff
objectclass: groupofuniquenames
uniquemember: cn=Manager,o=University of Michigan,c=US
uniquemember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=U
niversity of Michigan,c=US
uniquemember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
o=University of Michigan,c=US
uniquemember: cn=John Doe,ou=Information Technology Division,ou=People,o=Unive
rsity of Michigan,c=US
dn: cn=James A Jones 1,ou=Alumni Association,ou=People,o=University of Michiga
n,c=US
objectclass: OpenLDAPperson
cn: James A Jones 1
cn: James Jones
cn: Jim Jones
sn: Jones
uid: jaj
postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=US
userpassword:: amFq
homepostaladdress: 3882 Beverly Rd. $ Ann Arbor, MI 48105
homephone: +1 313 555 4772
description: Outstanding
title: Mad Cow Researcher, UM Alumni Association
pager: +1 313 555 3923
mail: jaj@mail.alumni.example.com
facsimiletelephonenumber: +1 313 555 4332
telephonenumber: +1 313 555 0895
dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Universi
ty of Michigan,c=US
objectclass: OpenLDAPperson
cn: James A Jones 2
cn: James Jones
cn: Jim Jones
sn: Doe
uid: jjones
seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=US
homepostaladdress: 933 Brooks $ Ann Arbor, MI 48104
homephone: +1 313 555 8838
title: Senior Manager, Information Technology Division
description: Not around very much
mail: jjones@mailgw.example.com
postaladdress: Info Tech Division $ 535 W William $ Ann Arbor, MI 48103
pager: +1 313 555 2833
facsimiletelephonenumber: +1 313 555 8688
telephonenumber: +1 313 555 7334
dn: cn=Jane Doe,ou=Alumni Association,ou=People,o=University of Michigan,c=US
objectclass: OpenLDAPperson
cn: Jane Doe
cn: Jane Alverson
sn: Doe
uid: jdoe
title: Programmer Analyst, UM Alumni Association
postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=US
homepostaladdress: 123 Anystreet $ Ann Arbor, MI 48104
drink: diet coke
description: Enthusiastic
mail: jdoe@woof.net
homephone: +1 313 555 5445
pager: +1 313 555 1220
facsimiletelephonenumber: +1 313 555 2311
telephonenumber: +1 313 555 4774
dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=University of Michigan
,c=US
objectclass: OpenLDAPperson
cn: Jennifer Smith
cn: Jen Smith
sn: Smith
uid: jen
postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=US
drink: Sam Adams
homepostaladdress: 1000 Maple #44 $ Ann Arbor, MI 48103
title: Telemarketer, UM Alumni Association
mail: jen@mail.alumni.example.com
homephone: +1 313 555 2333
pager: +1 313 555 6442
facsimiletelephonenumber: +1 313 555 2756
telephonenumber: +1 313 555 8232
dn: cn=John Doe,ou=Information Technology Division,ou=People,o=University of M
ichigan,c=US
objectclass: OpenLDAPperson
cn: John Doe
cn: Jonathon Doe
sn: Doe
uid: johnd
postaladdress: ITD $ 535 W. William $ Ann Arbor, MI 48109
seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=US
homepostaladdress: 912 East Bllvd $ Ann Arbor, MI 48104
title: System Administrator, Information Technology Division
description: overworked!
mail: johnd@mailgw.example.com
homephone: +1 313 555 3774
pager: +1 313 555 6573
facsimiletelephonenumber: +1 313 555 4544
telephonenumber: +1 313 555 9394
dn: cn=Manager,o=University of Michigan,c=US
objectclass: person
cn: Manager
cn: Directory Manager
cn: Dir Man
sn: Manager
description: Manager of the directory
userpassword:: c2VjcmV0
dn: cn=Mark Elliot,ou=Alumni Association,ou=People,o=University of Michigan,c=
US
objectclass: OpenLDAPperson
cn: Mark Elliot
cn: Mark A Elliot
sn: Elliot
uid: melliot
postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=US
homepostaladdress: 199 Outer Drive $ Ypsilanti, MI 48198
homephone: +1 313 555 0388
drink: Gasoline
title: Director, UM Alumni Association
mail: melliot@mail.alumni.example.com
pager: +1 313 555 7671
facsimiletelephonenumber: +1 313 555 7762
telephonenumber: +1 313 555 4177
dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Michiga
n,c=US
objectclass: OpenLDAPperson
cn: Ursula Hampster
sn: Hampster
uid: uham
title: Secretary, UM Alumni Association
postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=US
homepostaladdress: 123 Anystreet $ Ann Arbor, MI 48104
mail: uham@mail.alumni.example.com
homephone: +1 313 555 8421
pager: +1 313 555 2844
facsimiletelephonenumber: +1 313 555 9700
telephonenumber: +1 313 555 5331

View File

@ -0,0 +1,8 @@
foo=bar5
(&(|(a=b)(c=d))(e=f))
(&(|(a=b)(c=d)(g=h))(e=f))
name=firstname lastname
(&(sid=S-1-2-3)(name = fred bloggs))
(&(|(a=b)(c=d))(g=f))
(&(sid=S-1-2-3)(!(name = fred bloggs)))
(&(!(|(a=b)(c=d))(g=f)))

View File

@ -0,0 +1,5 @@
(blah=foo)
(objectclass=person)
(dn=*)
(&(objectclass=person)(objectclass=person))
(&(objectclass=person)(objectclass=personx))

View File

@ -0,0 +1,74 @@
/*
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: ldbadd
*
* Description: utility to add records - modelled on ldapadd
*
* Author: Andrew Tridgell
*/
#include "includes.h"
int main(void)
{
static struct ldb_context *ldb;
struct ldb_message *msg;
int ret;
int count=0, failures=0;
const char *ldb_url;
ldb_url = getenv("LDB_URL");
if (!ldb_url) {
ldb_url = "tdb://test.ldb";
}
ldb = ldb_connect(ldb_url, 0, NULL);
if (!ldb) {
perror("ldb_connect");
exit(1);
}
while ((msg = ldif_read_file(stdin))) {
ret = ldb_add(ldb, msg);
if (ret != 0) {
fprintf(stderr, "ERR: \"%s\" on DN %s\n",
ldb_errstring(ldb), msg->dn);
failures++;
} else {
count++;
}
ldif_read_free(msg);
}
ldb_close(ldb);
printf("Added %d records with %d failures\n", count, failures);
return 0;
}

View File

@ -0,0 +1,69 @@
/*
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: ldbdel
*
* Description: utility to delete records - modelled on ldapdelete
*
* Author: Andrew Tridgell
*/
#include "includes.h"
int main(int argc, const char *argv[])
{
static struct ldb_context *ldb;
int ret, i;
const char *ldb_url;
ldb_url = getenv("LDB_URL");
if (!ldb_url) {
ldb_url = "tdb://test.ldb";
}
if (argc < 2) {
printf("Usage: ldbdel <dn...>\n");
exit(1);
}
ldb = ldb_connect(ldb_url, 0, NULL);
if (!ldb) {
perror("ldb_connect");
exit(1);
}
for (i=1;i<argc;i++) {
ret = ldb_delete(ldb, argv[i]);
if (ret != 0) {
printf("delete of '%s' failed\n", argv[i]);
}
}
ldb_close(ldb);
return 0;
}

View File

@ -0,0 +1,122 @@
/*
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: ldbsearch
*
* Description: utility for ldb search - modelled on ldapsearch
*
* Author: Andrew Tridgell
*/
#include "includes.h"
#include <getopt.h>
int main(int argc, char * const argv[])
{
static struct ldb_context *ldb;
struct ldb_message **msgs;
int ret, i;
const char *expression;
const char * const *attrs = NULL;
const char *ldb_url;
const char *basedn = NULL;
int opt;
enum ldb_scope scope = LDB_SCOPE_DEFAULT;
ldb_url = getenv("LDB_URL");
if (!ldb_url) {
ldb_url = "tdb://test.ldb";
}
while ((opt = getopt(argc, argv, "b:H:s:")) != EOF) {
switch (opt) {
case 'b':
basedn = optarg;
break;
case 'H':
ldb_url = optarg;
break;
case 's':
if (strcmp(optarg, "base") == 0) {
scope = LDB_SCOPE_BASE;
} else if (strcmp(optarg, "sub") == 0) {
scope = LDB_SCOPE_SUBTREE;
} else if (strcmp(optarg, "one") == 0) {
scope = LDB_SCOPE_ONELEVEL;
}
break;
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
printf("Usage: ldbsearch <expression> [attrs...]\n");
exit(1);
}
if (argc > 1) {
attrs = argv+1;
}
expression = argv[0];
ldb = ldb_connect(ldb_url, 0, NULL);
if (!ldb) {
perror("ldb_connect");
exit(1);
}
ret = ldb_search(ldb, basedn, scope, expression, attrs, &msgs);
if (ret == -1) {
printf("search failed\n");
exit(1);
}
printf("# returned %d records\n", ret);
for (i=0;i<ret;i++) {
printf("# record %d\n", i+1);
ldif_write_file(stdout, msgs[i]);
}
if (ret > 0) {
ret = ldb_search_free(ldb, msgs);
if (ret == -1) {
fprintf(stderr, "search_free failed\n");
exit(1);
}
}
ldb_close(ldb);
return 0;
}