4 Copyright (C) Andrew Tridgell 2005
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2009
7 ** NOTE! The following LGPL license applies to the ldb
8 ** library. This does NOT imply that all of Samba is released
11 This library is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Lesser General Public
13 License as published by the Free Software Foundation; either
14 version 3 of the License, or (at your option) any later version.
16 This library is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Lesser General Public License for more details.
21 You should have received a copy of the GNU Lesser General Public
22 License along with this library; if not, see <http://www.gnu.org/licenses/>.
25 attribute handlers for well known attribute types, selected by syntax OID
29 #include "ldb_private.h"
30 #include "system/locale.h"
31 #include "ldb_handlers.h"
34 default handler that just copies a ldb_val.
36 int ldb_handler_copy(struct ldb_context *ldb, void *mem_ctx,
37 const struct ldb_val *in, struct ldb_val *out)
39 *out = ldb_val_dup(mem_ctx, in);
40 if (in->length > 0 && out->data == NULL) {
48 a case folding copy handler, removing leading and trailing spaces and
49 multiple internal spaces
51 We exploit the fact that utf8 never uses the space octet except for
54 int ldb_handler_fold(struct ldb_context *ldb, void *mem_ctx,
55 const struct ldb_val *in, struct ldb_val *out)
60 if (!in || !out || !(in->data)) {
64 out->data = (uint8_t *)ldb_casefold(ldb, mem_ctx, (const char *)(in->data), in->length);
65 if (out->data == NULL) {
66 ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_handler_fold: unable to casefold string [%.*s]", (int)in->length, (const char *)in->data);
70 s = (char *)(out->data);
72 /* remove trailing spaces if any */
74 while (l > 0 && s[l - 1] == ' ') l--;
77 /* remove leading spaces if any */
79 for (t = s; *s == ' '; s++) ;
81 /* remove leading spaces by moving down the string */
87 /* check middle spaces */
88 while ((t = strchr(s, ' ')) != NULL) {
89 for (s = t; *s == ' '; s++) ;
94 /* remove all spaces but one by moving down the string */
99 out->length = strlen((char *)out->data);
103 /* length limited conversion of a ldb_val to a int32_t */
104 static int val_to_int64(const struct ldb_val *in, int64_t *v)
109 /* make sure we don't read past the end of the data */
110 if (in->length > sizeof(buf)-1) {
111 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
113 strncpy(buf, (char *)in->data, in->length);
116 /* We've to use "strtoll" here to have the intended overflows.
117 * Otherwise we may get "LONG_MAX" and the conversion is wrong. */
118 *v = (int64_t) strtoll(buf, &end, 0);
120 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
127 canonicalise a ldap Integer
128 rfc2252 specifies it should be in decimal form
130 static int ldb_canonicalise_Integer(struct ldb_context *ldb, void *mem_ctx,
131 const struct ldb_val *in, struct ldb_val *out)
136 ret = val_to_int64(in, &i);
137 if (ret != LDB_SUCCESS) {
140 out->data = (uint8_t *) talloc_asprintf(mem_ctx, "%lld", (long long)i);
141 if (out->data == NULL) {
143 return LDB_ERR_OPERATIONS_ERROR;
145 out->length = strlen((char *)out->data);
152 static int ldb_comparison_Integer(struct ldb_context *ldb, void *mem_ctx,
153 const struct ldb_val *v1, const struct ldb_val *v2)
156 val_to_int64(v1, &i1);
157 val_to_int64(v2, &i2);
158 if (i1 == i2) return 0;
159 return i1 > i2? 1 : -1;
163 canonicalise a ldap Boolean
164 rfc2252 specifies it should be either "TRUE" or "FALSE"
166 static int ldb_canonicalise_Boolean(struct ldb_context *ldb, void *mem_ctx,
167 const struct ldb_val *in, struct ldb_val *out)
169 if (strncasecmp((char *)in->data, "TRUE", in->length) == 0) {
170 out->data = (uint8_t *)talloc_strdup(mem_ctx, "TRUE");
172 } else if (strncasecmp((char *)in->data, "FALSE", in->length) == 0) {
173 out->data = (uint8_t *)talloc_strdup(mem_ctx, "FALSE");
184 static int ldb_comparison_Boolean(struct ldb_context *ldb, void *mem_ctx,
185 const struct ldb_val *v1, const struct ldb_val *v2)
187 if (v1->length != v2->length) {
188 return v1->length - v2->length;
190 return strncasecmp((char *)v1->data, (char *)v2->data, v1->length);
195 compare two binary blobs
197 int ldb_comparison_binary(struct ldb_context *ldb, void *mem_ctx,
198 const struct ldb_val *v1, const struct ldb_val *v2)
200 if (v1->length != v2->length) {
201 return v1->length - v2->length;
203 return memcmp(v1->data, v2->data, v1->length);
207 compare two case insensitive strings, ignoring multiple whitespaces
208 and leading and trailing whitespaces
209 see rfc2252 section 8.1
211 try to optimize for the ascii case,
212 but if we find out an utf8 codepoint revert to slower but correct function
214 int ldb_comparison_fold(struct ldb_context *ldb, void *mem_ctx,
215 const struct ldb_val *v1, const struct ldb_val *v2)
217 const char *s1=(const char *)v1->data, *s2=(const char *)v2->data;
218 size_t n1 = v1->length, n2 = v2->length;
222 while (n1 && *s1 == ' ') { s1++; n1--; };
223 while (n2 && *s2 == ' ') { s2++; n2--; };
225 while (n1 && n2 && *s1 && *s2) {
226 /* the first 127 (0x7F) chars are ascii and utf8 guarantes they
227 * never appear in multibyte sequences */
228 if (((unsigned char)s1[0]) & 0x80) goto utf8str;
229 if (((unsigned char)s2[0]) & 0x80) goto utf8str;
230 if (toupper((unsigned char)*s1) != toupper((unsigned char)*s2))
233 while (n1 && s1[0] == s1[1]) { s1++; n1--; }
234 while (n2 && s2[0] == s2[1]) { s2++; n2--; }
240 /* check for trailing spaces only if the other pointers has
241 * reached the end of the strings otherwise we can
242 * mistakenly match. ex. "domain users" <->
245 if (n1 && *s1 == ' ' && (!n2 || !*s2)) {
246 while (n1 && *s1 == ' ') { s1++; n1--; }
248 if (n2 && *s2 == ' ' && (!n1 || !*s1)) {
249 while (n2 && *s2 == ' ') { s2++; n2--; }
251 if (n1 == 0 && n2 != 0) {
252 return -(int)toupper(*s2);
254 if (n2 == 0 && n1 != 0) {
255 return (int)toupper(*s1);
257 if (n2 == 0 && n2 == 0) {
260 return (int)toupper(*s1) - (int)toupper(*s2);
263 /* no need to recheck from the start, just from the first utf8 char found */
264 b1 = ldb_casefold(ldb, mem_ctx, s1, n1);
265 b2 = ldb_casefold(ldb, mem_ctx, s2, n2);
268 /* One of the strings was not UTF8, so we have no
269 * options but to do a binary compare */
272 if (memcmp(s1, s2, MIN(n1, n2)) == 0) {
273 if (n1 == n2) return 0;
275 return (int)toupper(s1[n2]);
277 return -(int)toupper(s2[n1]);
289 while (u1[0] == u1[1]) u1++;
290 while (u2[0] == u2[1]) u2++;
294 if (! (*u1 && *u2)) {
295 while (*u1 == ' ') u1++;
296 while (*u2 == ' ') u2++;
298 ret = (int)(*u1 - *u2);
308 canonicalise a attribute in DN format
310 static int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx,
311 const struct ldb_val *in, struct ldb_val *out)
319 dn = ldb_dn_from_ldb_val(mem_ctx, ldb, in);
320 if ( ! ldb_dn_validate(dn)) {
321 return LDB_ERR_INVALID_DN_SYNTAX;
324 out->data = (uint8_t *)ldb_dn_alloc_casefold(mem_ctx, dn);
325 if (out->data == NULL) {
328 out->length = strlen((char *)out->data);
341 static int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx,
342 const struct ldb_val *v1, const struct ldb_val *v2)
344 struct ldb_dn *dn1 = NULL, *dn2 = NULL;
347 dn1 = ldb_dn_from_ldb_val(mem_ctx, ldb, v1);
348 if ( ! ldb_dn_validate(dn1)) return -1;
350 dn2 = ldb_dn_from_ldb_val(mem_ctx, ldb, v2);
351 if ( ! ldb_dn_validate(dn2)) {
356 ret = ldb_dn_compare(dn1, dn2);
364 compare two utc time values. 1 second resolution
366 static int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx,
367 const struct ldb_val *v1, const struct ldb_val *v2)
370 ldb_val_to_time(v1, &t1);
371 ldb_val_to_time(v2, &t2);
372 if (t1 == t2) return 0;
373 return t1 > t2? 1 : -1;
377 canonicalise a utc time
379 static int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx,
380 const struct ldb_val *in, struct ldb_val *out)
384 ret = ldb_val_to_time(in, &t);
385 if (ret != LDB_SUCCESS) {
388 out->data = (uint8_t *)ldb_timestring(mem_ctx, t);
389 if (out->data == NULL) {
391 return LDB_ERR_OPERATIONS_ERROR;
393 out->length = strlen((char *)out->data);
398 table of standard attribute handlers
400 static const struct ldb_schema_syntax ldb_standard_syntaxes[] = {
402 .name = LDB_SYNTAX_INTEGER,
403 .ldif_read_fn = ldb_handler_copy,
404 .ldif_write_fn = ldb_handler_copy,
405 .canonicalise_fn = ldb_canonicalise_Integer,
406 .comparison_fn = ldb_comparison_Integer
409 .name = LDB_SYNTAX_OCTET_STRING,
410 .ldif_read_fn = ldb_handler_copy,
411 .ldif_write_fn = ldb_handler_copy,
412 .canonicalise_fn = ldb_handler_copy,
413 .comparison_fn = ldb_comparison_binary
416 .name = LDB_SYNTAX_DIRECTORY_STRING,
417 .ldif_read_fn = ldb_handler_copy,
418 .ldif_write_fn = ldb_handler_copy,
419 .canonicalise_fn = ldb_handler_fold,
420 .comparison_fn = ldb_comparison_fold
423 .name = LDB_SYNTAX_DN,
424 .ldif_read_fn = ldb_handler_copy,
425 .ldif_write_fn = ldb_handler_copy,
426 .canonicalise_fn = ldb_canonicalise_dn,
427 .comparison_fn = ldb_comparison_dn
430 .name = LDB_SYNTAX_OBJECTCLASS,
431 .ldif_read_fn = ldb_handler_copy,
432 .ldif_write_fn = ldb_handler_copy,
433 .canonicalise_fn = ldb_handler_fold,
434 .comparison_fn = ldb_comparison_fold
437 .name = LDB_SYNTAX_UTC_TIME,
438 .ldif_read_fn = ldb_handler_copy,
439 .ldif_write_fn = ldb_handler_copy,
440 .canonicalise_fn = ldb_canonicalise_utctime,
441 .comparison_fn = ldb_comparison_utctime
444 .name = LDB_SYNTAX_BOOLEAN,
445 .ldif_read_fn = ldb_handler_copy,
446 .ldif_write_fn = ldb_handler_copy,
447 .canonicalise_fn = ldb_canonicalise_Boolean,
448 .comparison_fn = ldb_comparison_Boolean
454 return the attribute handlers for a given syntax name
456 const struct ldb_schema_syntax *ldb_standard_syntax_by_name(struct ldb_context *ldb,
460 unsigned num_handlers = sizeof(ldb_standard_syntaxes)/sizeof(ldb_standard_syntaxes[0]);
461 /* TODO: should be replaced with a binary search */
462 for (i=0;i<num_handlers;i++) {
463 if (strcmp(ldb_standard_syntaxes[i].name, syntax) == 0) {
464 return &ldb_standard_syntaxes[i];
470 int ldb_any_comparison(struct ldb_context *ldb, void *mem_ctx,
471 ldb_attr_handler_t canonicalise_fn,
472 const struct ldb_val *v1,
473 const struct ldb_val *v2)
476 struct ldb_val v1_canon, v2_canon;
477 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
479 /* I could try and bail if tmp_ctx was NULL, but what return
482 * It seems easier to continue on the NULL context
484 ret1 = canonicalise_fn(ldb, tmp_ctx, v1, &v1_canon);
485 ret2 = canonicalise_fn(ldb, tmp_ctx, v2, &v2_canon);
487 if (ret1 == LDB_SUCCESS && ret2 == LDB_SUCCESS) {
488 ret = ldb_comparison_binary(ldb, mem_ctx, &v1_canon, &v2_canon);
490 ret = ldb_comparison_binary(ldb, mem_ctx, v1, v2);
492 talloc_free(tmp_ctx);