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
);
106 canonicalise a ldap Integer
107 rfc2252 specifies it should be in decimal form
109 static int ldb_canonicalise_Integer(struct ldb_context
*ldb
, void *mem_ctx
,
110 const struct ldb_val
*in
, struct ldb_val
*out
)
113 long long i
= strtoll((char *)in
->data
, &end
, 0);
117 out
->data
= (uint8_t *)talloc_asprintf(mem_ctx
, "%lld", i
);
118 if (out
->data
== NULL
) {
121 out
->length
= strlen((char *)out
->data
);
128 static int ldb_comparison_Integer(struct ldb_context
*ldb
, void *mem_ctx
,
129 const struct ldb_val
*v1
, const struct ldb_val
*v2
)
131 return strtoll((char *)v1
->data
, NULL
, 0) - strtoll((char *)v2
->data
, NULL
, 0);
135 canonicalise a ldap Boolean
136 rfc2252 specifies it should be either "TRUE" or "FALSE"
138 static int ldb_canonicalise_Boolean(struct ldb_context
*ldb
, void *mem_ctx
,
139 const struct ldb_val
*in
, struct ldb_val
*out
)
141 if (strncasecmp((char *)in
->data
, "TRUE", in
->length
) == 0) {
142 out
->data
= (uint8_t *)talloc_strdup(mem_ctx
, "TRUE");
144 } else if (strncasecmp((char *)in
->data
, "FALSE", in
->length
) == 0) {
145 out
->data
= (uint8_t *)talloc_strdup(mem_ctx
, "FALSE");
156 static int ldb_comparison_Boolean(struct ldb_context
*ldb
, void *mem_ctx
,
157 const struct ldb_val
*v1
, const struct ldb_val
*v2
)
159 if (v1
->length
!= v2
->length
) {
160 return v1
->length
- v2
->length
;
162 return strncasecmp((char *)v1
->data
, (char *)v2
->data
, v1
->length
);
167 compare two binary blobs
169 int ldb_comparison_binary(struct ldb_context
*ldb
, void *mem_ctx
,
170 const struct ldb_val
*v1
, const struct ldb_val
*v2
)
172 if (v1
->length
!= v2
->length
) {
173 return v1
->length
- v2
->length
;
175 return memcmp(v1
->data
, v2
->data
, v1
->length
);
179 compare two case insensitive strings, ignoring multiple whitespaces
180 and leading and trailing whitespaces
181 see rfc2252 section 8.1
183 try to optimize for the ascii case,
184 but if we find out an utf8 codepoint revert to slower but correct function
186 int ldb_comparison_fold(struct ldb_context
*ldb
, void *mem_ctx
,
187 const struct ldb_val
*v1
, const struct ldb_val
*v2
)
189 const char *s1
=(const char *)v1
->data
, *s2
=(const char *)v2
->data
;
190 size_t n1
= v1
->length
, n2
= v2
->length
;
194 while (n1
&& *s1
== ' ') { s1
++; n1
--; };
195 while (n2
&& *s2
== ' ') { s2
++; n2
--; };
197 while (n1
&& n2
&& *s1
&& *s2
) {
198 /* the first 127 (0x7F) chars are ascii and utf8 guarantes they
199 * never appear in multibyte sequences */
200 if (((unsigned char)s1
[0]) & 0x80) goto utf8str
;
201 if (((unsigned char)s2
[0]) & 0x80) goto utf8str
;
202 if (toupper((unsigned char)*s1
) != toupper((unsigned char)*s2
))
205 while (n1
&& s1
[0] == s1
[1]) { s1
++; n1
--; }
206 while (n2
&& s2
[0] == s2
[1]) { s2
++; n2
--; }
212 /* check for trailing spaces only if the other pointers has
213 * reached the end of the strings otherwise we can
214 * mistakenly match. ex. "domain users" <->
217 if (n1
&& *s1
== ' ' && (!n2
|| !*s2
)) {
218 while (n1
&& *s1
== ' ') { s1
++; n1
--; }
220 if (n2
&& *s2
== ' ' && (!n1
|| !*s1
)) {
221 while (n2
&& *s2
== ' ') { s2
++; n2
--; }
223 if (n1
== 0 && n2
!= 0) {
224 return -(int)toupper(*s2
);
226 if (n2
== 0 && n1
!= 0) {
227 return (int)toupper(*s1
);
229 if (n2
== 0 && n2
== 0) {
232 return (int)toupper(*s1
) - (int)toupper(*s2
);
235 /* no need to recheck from the start, just from the first utf8 char found */
236 b1
= ldb_casefold(ldb
, mem_ctx
, s1
, n1
);
237 b2
= ldb_casefold(ldb
, mem_ctx
, s2
, n2
);
240 /* One of the strings was not UTF8, so we have no
241 * options but to do a binary compare */
244 if (memcmp(s1
, s2
, MIN(n1
, n2
)) == 0) {
245 if (n1
== n2
) return 0;
247 return (int)toupper(s1
[n2
]);
249 return -(int)toupper(s2
[n1
]);
261 while (u1
[0] == u1
[1]) u1
++;
262 while (u2
[0] == u2
[1]) u2
++;
266 if (! (*u1
&& *u2
)) {
267 while (*u1
== ' ') u1
++;
268 while (*u2
== ' ') u2
++;
270 ret
= (int)(*u1
- *u2
);
280 canonicalise a attribute in DN format
282 static int ldb_canonicalise_dn(struct ldb_context
*ldb
, void *mem_ctx
,
283 const struct ldb_val
*in
, struct ldb_val
*out
)
291 dn
= ldb_dn_from_ldb_val(mem_ctx
, ldb
, in
);
292 if ( ! ldb_dn_validate(dn
)) {
293 return LDB_ERR_INVALID_DN_SYNTAX
;
296 out
->data
= (uint8_t *)ldb_dn_alloc_casefold(mem_ctx
, dn
);
297 if (out
->data
== NULL
) {
300 out
->length
= strlen((char *)out
->data
);
313 static int ldb_comparison_dn(struct ldb_context
*ldb
, void *mem_ctx
,
314 const struct ldb_val
*v1
, const struct ldb_val
*v2
)
316 struct ldb_dn
*dn1
= NULL
, *dn2
= NULL
;
319 dn1
= ldb_dn_from_ldb_val(mem_ctx
, ldb
, v1
);
320 if ( ! ldb_dn_validate(dn1
)) return -1;
322 dn2
= ldb_dn_from_ldb_val(mem_ctx
, ldb
, v2
);
323 if ( ! ldb_dn_validate(dn2
)) {
328 ret
= ldb_dn_compare(dn1
, dn2
);
336 compare two utc time values. 1 second resolution
338 static int ldb_comparison_utctime(struct ldb_context
*ldb
, void *mem_ctx
,
339 const struct ldb_val
*v1
, const struct ldb_val
*v2
)
342 t1
= ldb_string_to_time((char *)v1
->data
);
343 t2
= ldb_string_to_time((char *)v2
->data
);
344 return (int)t2
- (int)t1
;
348 canonicalise a utc time
350 static int ldb_canonicalise_utctime(struct ldb_context
*ldb
, void *mem_ctx
,
351 const struct ldb_val
*in
, struct ldb_val
*out
)
353 time_t t
= ldb_string_to_time((char *)in
->data
);
354 out
->data
= (uint8_t *)ldb_timestring(mem_ctx
, t
);
355 if (out
->data
== NULL
) {
358 out
->length
= strlen((char *)out
->data
);
363 table of standard attribute handlers
365 static const struct ldb_schema_syntax ldb_standard_syntaxes
[] = {
367 .name
= LDB_SYNTAX_INTEGER
,
368 .ldif_read_fn
= ldb_handler_copy
,
369 .ldif_write_fn
= ldb_handler_copy
,
370 .canonicalise_fn
= ldb_canonicalise_Integer
,
371 .comparison_fn
= ldb_comparison_Integer
374 .name
= LDB_SYNTAX_OCTET_STRING
,
375 .ldif_read_fn
= ldb_handler_copy
,
376 .ldif_write_fn
= ldb_handler_copy
,
377 .canonicalise_fn
= ldb_handler_copy
,
378 .comparison_fn
= ldb_comparison_binary
381 .name
= LDB_SYNTAX_DIRECTORY_STRING
,
382 .ldif_read_fn
= ldb_handler_copy
,
383 .ldif_write_fn
= ldb_handler_copy
,
384 .canonicalise_fn
= ldb_handler_fold
,
385 .comparison_fn
= ldb_comparison_fold
388 .name
= LDB_SYNTAX_DN
,
389 .ldif_read_fn
= ldb_handler_copy
,
390 .ldif_write_fn
= ldb_handler_copy
,
391 .canonicalise_fn
= ldb_canonicalise_dn
,
392 .comparison_fn
= ldb_comparison_dn
395 .name
= LDB_SYNTAX_OBJECTCLASS
,
396 .ldif_read_fn
= ldb_handler_copy
,
397 .ldif_write_fn
= ldb_handler_copy
,
398 .canonicalise_fn
= ldb_handler_fold
,
399 .comparison_fn
= ldb_comparison_fold
402 .name
= LDB_SYNTAX_UTC_TIME
,
403 .ldif_read_fn
= ldb_handler_copy
,
404 .ldif_write_fn
= ldb_handler_copy
,
405 .canonicalise_fn
= ldb_canonicalise_utctime
,
406 .comparison_fn
= ldb_comparison_utctime
409 .name
= LDB_SYNTAX_BOOLEAN
,
410 .ldif_read_fn
= ldb_handler_copy
,
411 .ldif_write_fn
= ldb_handler_copy
,
412 .canonicalise_fn
= ldb_canonicalise_Boolean
,
413 .comparison_fn
= ldb_comparison_Boolean
419 return the attribute handlers for a given syntax name
421 const struct ldb_schema_syntax
*ldb_standard_syntax_by_name(struct ldb_context
*ldb
,
425 unsigned num_handlers
= sizeof(ldb_standard_syntaxes
)/sizeof(ldb_standard_syntaxes
[0]);
426 /* TODO: should be replaced with a binary search */
427 for (i
=0;i
<num_handlers
;i
++) {
428 if (strcmp(ldb_standard_syntaxes
[i
].name
, syntax
) == 0) {
429 return &ldb_standard_syntaxes
[i
];
435 int ldb_any_comparison(struct ldb_context
*ldb
, void *mem_ctx
,
436 ldb_attr_handler_t canonicalise_fn
,
437 const struct ldb_val
*v1
,
438 const struct ldb_val
*v2
)
441 struct ldb_val v1_canon
, v2_canon
;
442 TALLOC_CTX
*tmp_ctx
= talloc_new(mem_ctx
);
444 /* I could try and bail if tmp_ctx was NULL, but what return
447 * It seems easier to continue on the NULL context
449 ret1
= canonicalise_fn(ldb
, tmp_ctx
, v1
, &v1_canon
);
450 ret2
= canonicalise_fn(ldb
, tmp_ctx
, v2
, &v2_canon
);
452 if (ret1
== LDB_SUCCESS
&& ret2
== LDB_SUCCESS
) {
453 ret
= ldb_comparison_binary(ldb
, mem_ctx
, &v1_canon
, &v2_canon
);
455 ret
= ldb_comparison_binary(ldb
, mem_ctx
, v1
, v2
);
457 talloc_free(tmp_ctx
);