ldb: fix the canonicalisation of booleans
[amitay/samba.git] / lib / ldb / common / attrib_handlers.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Tridgell  2005
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2009
6
7      ** NOTE! The following LGPL license applies to the ldb
8      ** library. This does NOT imply that all of Samba is released
9      ** under the LGPL
10    
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.
15
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.
20
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/>.
23 */
24 /*
25   attribute handlers for well known attribute types, selected by syntax OID
26   see rfc2252
27 */
28
29 #include "ldb_private.h"
30 #include "system/locale.h"
31 #include "ldb_handlers.h"
32
33 /*
34   default handler that just copies a ldb_val.
35 */
36 int ldb_handler_copy(struct ldb_context *ldb, void *mem_ctx,
37                      const struct ldb_val *in, struct ldb_val *out)
38 {
39         *out = ldb_val_dup(mem_ctx, in);
40         if (in->length > 0 && out->data == NULL) {
41                 ldb_oom(ldb);
42                 return -1;
43         }
44         return 0;
45 }
46
47 /*
48   a case folding copy handler, removing leading and trailing spaces and
49   multiple internal spaces
50
51   We exploit the fact that utf8 never uses the space octet except for
52   the space itself
53 */
54 int ldb_handler_fold(struct ldb_context *ldb, void *mem_ctx,
55                             const struct ldb_val *in, struct ldb_val *out)
56 {
57         char *s, *t;
58         size_t l;
59
60         if (!in || !out || !(in->data)) {
61                 return -1;
62         }
63
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);
67                 return -1;
68         }
69
70         s = (char *)(out->data);
71         
72         /* remove trailing spaces if any */
73         l = strlen(s);
74         while (l > 0 && s[l - 1] == ' ') l--;
75         s[l] = '\0';
76         
77         /* remove leading spaces if any */
78         if (*s == ' ') {
79                 for (t = s; *s == ' '; s++) ;
80
81                 /* remove leading spaces by moving down the string */
82                 memmove(t, s, l);
83
84                 s = t;
85         }
86
87         /* check middle spaces */
88         while ((t = strchr(s, ' ')) != NULL) {
89                 for (s = t; *s == ' '; s++) ;
90
91                 if ((s - t) > 1) {
92                         l = strlen(s);
93
94                         /* remove all spaces but one by moving down the string */
95                         memmove(t + 1, s, l);
96                 }
97         }
98
99         out->length = strlen((char *)out->data);
100         return 0;
101 }
102
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)
105 {
106         char *end;
107         char buf[64];
108
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;
112         }
113         strncpy(buf, (char *)in->data, in->length);
114         buf[in->length] = 0;
115
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);
119         if (*end != 0) {
120                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
121         }
122         return LDB_SUCCESS;
123 }
124
125
126 /*
127   canonicalise a ldap Integer
128   rfc2252 specifies it should be in decimal form
129 */
130 static int ldb_canonicalise_Integer(struct ldb_context *ldb, void *mem_ctx,
131                                     const struct ldb_val *in, struct ldb_val *out)
132 {
133         int64_t i;
134         int ret;
135
136         ret = val_to_int64(in, &i);
137         if (ret != LDB_SUCCESS) {
138                 return ret;
139         }
140         out->data = (uint8_t *) talloc_asprintf(mem_ctx, "%lld", (long long)i);
141         if (out->data == NULL) {
142                 ldb_oom(ldb);
143                 return LDB_ERR_OPERATIONS_ERROR;
144         }
145         out->length = strlen((char *)out->data);
146         return 0;
147 }
148
149 /*
150   compare two Integers
151 */
152 static int ldb_comparison_Integer(struct ldb_context *ldb, void *mem_ctx,
153                                   const struct ldb_val *v1, const struct ldb_val *v2)
154 {
155         int64_t i1=0, i2=0;
156         val_to_int64(v1, &i1);
157         val_to_int64(v2, &i2);
158         if (i1 == i2) return 0;
159         return i1 > i2? 1 : -1;
160 }
161
162 /*
163   canonicalise a ldap Boolean
164   rfc2252 specifies it should be either "TRUE" or "FALSE"
165 */
166 static int ldb_canonicalise_Boolean(struct ldb_context *ldb, void *mem_ctx,
167                              const struct ldb_val *in, struct ldb_val *out)
168 {
169         if (in->length >= 4 && strncasecmp((char *)in->data, "TRUE", in->length) == 0) {
170                 out->data = (uint8_t *)talloc_strdup(mem_ctx, "TRUE");
171                 out->length = 4;
172         } else if (in->length >= 5 && strncasecmp((char *)in->data, "FALSE", in->length) == 0) {
173                 out->data = (uint8_t *)talloc_strdup(mem_ctx, "FALSE");
174                 out->length = 5;
175         } else {
176                 return -1;
177         }
178         return 0;
179 }
180
181 /*
182   compare two Booleans
183 */
184 static int ldb_comparison_Boolean(struct ldb_context *ldb, void *mem_ctx,
185                            const struct ldb_val *v1, const struct ldb_val *v2)
186 {
187         if (v1->length != v2->length) {
188                 return v1->length - v2->length;
189         }
190         return strncasecmp((char *)v1->data, (char *)v2->data, v1->length);
191 }
192
193
194 /*
195   compare two binary blobs
196 */
197 int ldb_comparison_binary(struct ldb_context *ldb, void *mem_ctx,
198                           const struct ldb_val *v1, const struct ldb_val *v2)
199 {
200         if (v1->length != v2->length) {
201                 return v1->length - v2->length;
202         }
203         return memcmp(v1->data, v2->data, v1->length);
204 }
205
206 /*
207   compare two case insensitive strings, ignoring multiple whitespaces
208   and leading and trailing whitespaces
209   see rfc2252 section 8.1
210         
211   try to optimize for the ascii case,
212   but if we find out an utf8 codepoint revert to slower but correct function
213 */
214 int ldb_comparison_fold(struct ldb_context *ldb, void *mem_ctx,
215                                const struct ldb_val *v1, const struct ldb_val *v2)
216 {
217         const char *s1=(const char *)v1->data, *s2=(const char *)v2->data;
218         size_t n1 = v1->length, n2 = v2->length;
219         char *b1, *b2;
220         const char *u1, *u2;
221         int ret;
222         while (n1 && *s1 == ' ') { s1++; n1--; };
223         while (n2 && *s2 == ' ') { s2++; n2--; };
224
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))
231                         break;
232                 if (*s1 == ' ') {
233                         while (n1 && s1[0] == s1[1]) { s1++; n1--; }
234                         while (n2 && s2[0] == s2[1]) { s2++; n2--; }
235                 }
236                 s1++; s2++;
237                 n1--; n2--;
238         }
239
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" <->
243          * "domainUpdates"
244          */
245         if (n1 && *s1 == ' ' && (!n2 || !*s2)) {
246                 while (n1 && *s1 == ' ') { s1++; n1--; }                
247         }
248         if (n2 && *s2 == ' ' && (!n1 || !*s1)) {
249                 while (n2 && *s2 == ' ') { s2++; n2--; }                
250         }
251         if (n1 == 0 && n2 != 0) {
252                 return -(int)toupper(*s2);
253         }
254         if (n2 == 0 && n1 != 0) {
255                 return (int)toupper(*s1);
256         }
257         if (n2 == 0 && n2 == 0) {
258                 return 0;
259         }
260         return (int)toupper(*s1) - (int)toupper(*s2);
261
262 utf8str:
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);
266
267         if (!b1 || !b2) {
268                 /* One of the strings was not UTF8, so we have no
269                  * options but to do a binary compare */
270                 talloc_free(b1);
271                 talloc_free(b2);
272                 ret = memcmp(s1, s2, MIN(n1, n2));
273                 if (ret == 0) {
274                         if (n1 == n2) return 0;
275                         if (n1 > n2) {
276                                 return (int)toupper(s1[n2]);
277                         } else {
278                                 return -(int)toupper(s2[n1]);
279                         }
280                 }
281                 return ret;
282         }
283
284         u1 = b1;
285         u2 = b2;
286
287         while (*u1 & *u2) {
288                 if (*u1 != *u2)
289                         break;
290                 if (*u1 == ' ') {
291                         while (u1[0] == u1[1]) u1++;
292                         while (u2[0] == u2[1]) u2++;
293                 }
294                 u1++; u2++;
295         }
296         if (! (*u1 && *u2)) {
297                 while (*u1 == ' ') u1++;
298                 while (*u2 == ' ') u2++;
299         }
300         ret = (int)(*u1 - *u2);
301
302         talloc_free(b1);
303         talloc_free(b2);
304         
305         return ret;
306 }
307
308
309 /*
310   canonicalise a attribute in DN format
311 */
312 static int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx,
313                                const struct ldb_val *in, struct ldb_val *out)
314 {
315         struct ldb_dn *dn;
316         int ret = -1;
317
318         out->length = 0;
319         out->data = NULL;
320
321         dn = ldb_dn_from_ldb_val(mem_ctx, ldb, in);
322         if ( ! ldb_dn_validate(dn)) {
323                 return LDB_ERR_INVALID_DN_SYNTAX;
324         }
325
326         out->data = (uint8_t *)ldb_dn_alloc_casefold(mem_ctx, dn);
327         if (out->data == NULL) {
328                 goto done;
329         }
330         out->length = strlen((char *)out->data);
331
332         ret = 0;
333
334 done:
335         talloc_free(dn);
336
337         return ret;
338 }
339
340 /*
341   compare two dns
342 */
343 static int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx,
344                              const struct ldb_val *v1, const struct ldb_val *v2)
345 {
346         struct ldb_dn *dn1 = NULL, *dn2 = NULL;
347         int ret;
348
349         dn1 = ldb_dn_from_ldb_val(mem_ctx, ldb, v1);
350         if ( ! ldb_dn_validate(dn1)) return -1;
351
352         dn2 = ldb_dn_from_ldb_val(mem_ctx, ldb, v2);
353         if ( ! ldb_dn_validate(dn2)) {
354                 talloc_free(dn1);
355                 return -1;
356         } 
357
358         ret = ldb_dn_compare(dn1, dn2);
359
360         talloc_free(dn1);
361         talloc_free(dn2);
362         return ret;
363 }
364
365 /*
366   compare two utc time values. 1 second resolution
367 */
368 static int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx,
369                                   const struct ldb_val *v1, const struct ldb_val *v2)
370 {
371         time_t t1=0, t2=0;
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;
376 }
377
378 /*
379   canonicalise a utc time
380 */
381 static int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx,
382                                     const struct ldb_val *in, struct ldb_val *out)
383 {
384         time_t t;
385         int ret;
386         ret = ldb_val_to_time(in, &t);
387         if (ret != LDB_SUCCESS) {
388                 return ret;
389         }
390         out->data = (uint8_t *)ldb_timestring(mem_ctx, t);
391         if (out->data == NULL) {
392                 ldb_oom(ldb);
393                 return LDB_ERR_OPERATIONS_ERROR;
394         }
395         out->length = strlen((char *)out->data);
396         return 0;
397 }
398
399 /*
400   table of standard attribute handlers
401 */
402 static const struct ldb_schema_syntax ldb_standard_syntaxes[] = {
403         { 
404                 .name            = LDB_SYNTAX_INTEGER,
405                 .ldif_read_fn    = ldb_handler_copy,
406                 .ldif_write_fn   = ldb_handler_copy,
407                 .canonicalise_fn = ldb_canonicalise_Integer,
408                 .comparison_fn   = ldb_comparison_Integer
409         },
410         { 
411                 .name            = LDB_SYNTAX_OCTET_STRING,
412                 .ldif_read_fn    = ldb_handler_copy,
413                 .ldif_write_fn   = ldb_handler_copy,
414                 .canonicalise_fn = ldb_handler_copy,
415                 .comparison_fn   = ldb_comparison_binary
416         },
417         { 
418                 .name            = LDB_SYNTAX_DIRECTORY_STRING,
419                 .ldif_read_fn    = ldb_handler_copy,
420                 .ldif_write_fn   = ldb_handler_copy,
421                 .canonicalise_fn = ldb_handler_fold,
422                 .comparison_fn   = ldb_comparison_fold
423         },
424         { 
425                 .name            = LDB_SYNTAX_DN,
426                 .ldif_read_fn    = ldb_handler_copy,
427                 .ldif_write_fn   = ldb_handler_copy,
428                 .canonicalise_fn = ldb_canonicalise_dn,
429                 .comparison_fn   = ldb_comparison_dn
430         },
431         { 
432                 .name            = LDB_SYNTAX_OBJECTCLASS,
433                 .ldif_read_fn    = ldb_handler_copy,
434                 .ldif_write_fn   = ldb_handler_copy,
435                 .canonicalise_fn = ldb_handler_fold,
436                 .comparison_fn   = ldb_comparison_fold
437         },
438         { 
439                 .name            = LDB_SYNTAX_UTC_TIME,
440                 .ldif_read_fn    = ldb_handler_copy,
441                 .ldif_write_fn   = ldb_handler_copy,
442                 .canonicalise_fn = ldb_canonicalise_utctime,
443                 .comparison_fn   = ldb_comparison_utctime
444         },
445         { 
446                 .name            = LDB_SYNTAX_BOOLEAN,
447                 .ldif_read_fn    = ldb_handler_copy,
448                 .ldif_write_fn   = ldb_handler_copy,
449                 .canonicalise_fn = ldb_canonicalise_Boolean,
450                 .comparison_fn   = ldb_comparison_Boolean
451         },
452 };
453
454
455 /*
456   return the attribute handlers for a given syntax name
457 */
458 const struct ldb_schema_syntax *ldb_standard_syntax_by_name(struct ldb_context *ldb,
459                                                             const char *syntax)
460 {
461         unsigned int i;
462         unsigned num_handlers = sizeof(ldb_standard_syntaxes)/sizeof(ldb_standard_syntaxes[0]);
463         /* TODO: should be replaced with a binary search */
464         for (i=0;i<num_handlers;i++) {
465                 if (strcmp(ldb_standard_syntaxes[i].name, syntax) == 0) {
466                         return &ldb_standard_syntaxes[i];
467                 }
468         }
469         return NULL;
470 }
471
472 int ldb_any_comparison(struct ldb_context *ldb, void *mem_ctx, 
473                        ldb_attr_handler_t canonicalise_fn, 
474                        const struct ldb_val *v1,
475                        const struct ldb_val *v2)
476 {
477         int ret, ret1, ret2;
478         struct ldb_val v1_canon, v2_canon;
479         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
480
481         /* I could try and bail if tmp_ctx was NULL, but what return
482          * value would I use?
483          *
484          * It seems easier to continue on the NULL context 
485          */
486         ret1 = canonicalise_fn(ldb, tmp_ctx, v1, &v1_canon);
487         ret2 = canonicalise_fn(ldb, tmp_ctx, v2, &v2_canon);
488
489         if (ret1 == LDB_SUCCESS && ret2 == LDB_SUCCESS) {
490                 ret = ldb_comparison_binary(ldb, mem_ctx, &v1_canon, &v2_canon);
491         } else {
492                 ret = ldb_comparison_binary(ldb, mem_ctx, v1, v2);
493         }
494         talloc_free(tmp_ctx);
495         return ret;
496 }