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 (in
->length
>= 4 && strncasecmp((char *)in
->data
, "TRUE", in
->length
) == 0) {
170 out
->data
= (uint8_t *)talloc_strdup(mem_ctx
, "TRUE");
172 } else if (in
->length
>= 5 && 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 (n1
== 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 ret
= memcmp(s1
, s2
, MIN(n1
, n2
));
274 if (n1
== n2
) return 0;
276 return (int)toupper(s1
[n2
]);
278 return -(int)toupper(s2
[n1
]);
291 while (u1
[0] == u1
[1]) u1
++;
292 while (u2
[0] == u2
[1]) u2
++;
296 if (! (*u1
&& *u2
)) {
297 while (*u1
== ' ') u1
++;
298 while (*u2
== ' ') u2
++;
300 ret
= (int)(*u1
- *u2
);
310 canonicalise a attribute in DN format
312 static int ldb_canonicalise_dn(struct ldb_context
*ldb
, void *mem_ctx
,
313 const struct ldb_val
*in
, struct ldb_val
*out
)
321 dn
= ldb_dn_from_ldb_val(mem_ctx
, ldb
, in
);
322 if ( ! ldb_dn_validate(dn
)) {
323 return LDB_ERR_INVALID_DN_SYNTAX
;
326 out
->data
= (uint8_t *)ldb_dn_alloc_casefold(mem_ctx
, dn
);
327 if (out
->data
== NULL
) {
330 out
->length
= strlen((char *)out
->data
);
343 static int ldb_comparison_dn(struct ldb_context
*ldb
, void *mem_ctx
,
344 const struct ldb_val
*v1
, const struct ldb_val
*v2
)
346 struct ldb_dn
*dn1
= NULL
, *dn2
= NULL
;
349 dn1
= ldb_dn_from_ldb_val(mem_ctx
, ldb
, v1
);
350 if ( ! ldb_dn_validate(dn1
)) return -1;
352 dn2
= ldb_dn_from_ldb_val(mem_ctx
, ldb
, v2
);
353 if ( ! ldb_dn_validate(dn2
)) {
358 ret
= ldb_dn_compare(dn1
, dn2
);
366 compare two utc time values. 1 second resolution
368 static int ldb_comparison_utctime(struct ldb_context
*ldb
, void *mem_ctx
,
369 const struct ldb_val
*v1
, const struct ldb_val
*v2
)
372 ldb_val_to_time(v1
, &t1
);
373 ldb_val_to_time(v2
, &t2
);
374 if (t1
== t2
) return 0;
375 return t1
> t2
? 1 : -1;
379 canonicalise a utc time
381 static int ldb_canonicalise_utctime(struct ldb_context
*ldb
, void *mem_ctx
,
382 const struct ldb_val
*in
, struct ldb_val
*out
)
386 ret
= ldb_val_to_time(in
, &t
);
387 if (ret
!= LDB_SUCCESS
) {
390 out
->data
= (uint8_t *)ldb_timestring_utc(mem_ctx
, t
);
391 if (out
->data
== NULL
) {
393 return LDB_ERR_OPERATIONS_ERROR
;
395 out
->length
= strlen((char *)out
->data
);
400 canonicalise a generalized time
402 static int ldb_canonicalise_generalizedtime(struct ldb_context
*ldb
, void *mem_ctx
,
403 const struct ldb_val
*in
, struct ldb_val
*out
)
407 ret
= ldb_val_to_time(in
, &t
);
408 if (ret
!= LDB_SUCCESS
) {
411 out
->data
= (uint8_t *)ldb_timestring(mem_ctx
, t
);
412 if (out
->data
== NULL
) {
414 return LDB_ERR_OPERATIONS_ERROR
;
416 out
->length
= strlen((char *)out
->data
);
421 table of standard attribute handlers
423 static const struct ldb_schema_syntax ldb_standard_syntaxes
[] = {
425 .name
= LDB_SYNTAX_INTEGER
,
426 .ldif_read_fn
= ldb_handler_copy
,
427 .ldif_write_fn
= ldb_handler_copy
,
428 .canonicalise_fn
= ldb_canonicalise_Integer
,
429 .comparison_fn
= ldb_comparison_Integer
432 .name
= LDB_SYNTAX_OCTET_STRING
,
433 .ldif_read_fn
= ldb_handler_copy
,
434 .ldif_write_fn
= ldb_handler_copy
,
435 .canonicalise_fn
= ldb_handler_copy
,
436 .comparison_fn
= ldb_comparison_binary
439 .name
= LDB_SYNTAX_DIRECTORY_STRING
,
440 .ldif_read_fn
= ldb_handler_copy
,
441 .ldif_write_fn
= ldb_handler_copy
,
442 .canonicalise_fn
= ldb_handler_fold
,
443 .comparison_fn
= ldb_comparison_fold
446 .name
= LDB_SYNTAX_DN
,
447 .ldif_read_fn
= ldb_handler_copy
,
448 .ldif_write_fn
= ldb_handler_copy
,
449 .canonicalise_fn
= ldb_canonicalise_dn
,
450 .comparison_fn
= ldb_comparison_dn
453 .name
= LDB_SYNTAX_OBJECTCLASS
,
454 .ldif_read_fn
= ldb_handler_copy
,
455 .ldif_write_fn
= ldb_handler_copy
,
456 .canonicalise_fn
= ldb_handler_fold
,
457 .comparison_fn
= ldb_comparison_fold
460 .name
= LDB_SYNTAX_UTC_TIME
,
461 .ldif_read_fn
= ldb_handler_copy
,
462 .ldif_write_fn
= ldb_handler_copy
,
463 .canonicalise_fn
= ldb_canonicalise_utctime
,
464 .comparison_fn
= ldb_comparison_utctime
467 .name
= LDB_SYNTAX_GENERALIZED_TIME
,
468 .ldif_read_fn
= ldb_handler_copy
,
469 .ldif_write_fn
= ldb_handler_copy
,
470 .canonicalise_fn
= ldb_canonicalise_generalizedtime
,
471 .comparison_fn
= ldb_comparison_utctime
474 .name
= LDB_SYNTAX_BOOLEAN
,
475 .ldif_read_fn
= ldb_handler_copy
,
476 .ldif_write_fn
= ldb_handler_copy
,
477 .canonicalise_fn
= ldb_canonicalise_Boolean
,
478 .comparison_fn
= ldb_comparison_Boolean
484 return the attribute handlers for a given syntax name
486 const struct ldb_schema_syntax
*ldb_standard_syntax_by_name(struct ldb_context
*ldb
,
490 unsigned num_handlers
= sizeof(ldb_standard_syntaxes
)/sizeof(ldb_standard_syntaxes
[0]);
491 /* TODO: should be replaced with a binary search */
492 for (i
=0;i
<num_handlers
;i
++) {
493 if (strcmp(ldb_standard_syntaxes
[i
].name
, syntax
) == 0) {
494 return &ldb_standard_syntaxes
[i
];
500 int ldb_any_comparison(struct ldb_context
*ldb
, void *mem_ctx
,
501 ldb_attr_handler_t canonicalise_fn
,
502 const struct ldb_val
*v1
,
503 const struct ldb_val
*v2
)
506 struct ldb_val v1_canon
, v2_canon
;
507 TALLOC_CTX
*tmp_ctx
= talloc_new(mem_ctx
);
509 /* I could try and bail if tmp_ctx was NULL, but what return
512 * It seems easier to continue on the NULL context
514 ret1
= canonicalise_fn(ldb
, tmp_ctx
, v1
, &v1_canon
);
515 ret2
= canonicalise_fn(ldb
, tmp_ctx
, v2
, &v2_canon
);
517 if (ret1
== LDB_SUCCESS
&& ret2
== LDB_SUCCESS
) {
518 ret
= ldb_comparison_binary(ldb
, mem_ctx
, &v1_canon
, &v2_canon
);
520 ret
= ldb_comparison_binary(ldb
, mem_ctx
, v1
, v2
);
522 talloc_free(tmp_ctx
);