2a2bd0852e594d5931e82d17972463f89aeed3e6
[bbaumbach/samba-autobuild/.git] / source4 / 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 (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 (strncasecmp((char *)in->data, "FALSE", in->length) == 0) {
173                 out->data = (uint8_t *)talloc_strdup(mem_ctx, "FALSE");
174                 out->length = 4;
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                 if (memcmp(s1, s2, MIN(n1, n2)) == 0) {
273                         if (n1 == n2) return 0;
274                         if (n1 > n2) {
275                                 return (int)toupper(s1[n2]);
276                         } else {
277                                 return -(int)toupper(s2[n1]);
278                         }
279                 }
280         }
281
282         u1 = b1;
283         u2 = b2;
284
285         while (*u1 & *u2) {
286                 if (*u1 != *u2)
287                         break;
288                 if (*u1 == ' ') {
289                         while (u1[0] == u1[1]) u1++;
290                         while (u2[0] == u2[1]) u2++;
291                 }
292                 u1++; u2++;
293         }
294         if (! (*u1 && *u2)) {
295                 while (*u1 == ' ') u1++;
296                 while (*u2 == ' ') u2++;
297         }
298         ret = (int)(*u1 - *u2);
299
300         talloc_free(b1);
301         talloc_free(b2);
302         
303         return ret;
304 }
305
306
307 /*
308   canonicalise a attribute in DN format
309 */
310 static int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx,
311                                const struct ldb_val *in, struct ldb_val *out)
312 {
313         struct ldb_dn *dn;
314         int ret = -1;
315
316         out->length = 0;
317         out->data = NULL;
318
319         dn = ldb_dn_from_ldb_val(mem_ctx, ldb, in);
320         if ( ! ldb_dn_validate(dn)) {
321                 return LDB_ERR_INVALID_DN_SYNTAX;
322         }
323
324         out->data = (uint8_t *)ldb_dn_alloc_casefold(mem_ctx, dn);
325         if (out->data == NULL) {
326                 goto done;
327         }
328         out->length = strlen((char *)out->data);
329
330         ret = 0;
331
332 done:
333         talloc_free(dn);
334
335         return ret;
336 }
337
338 /*
339   compare two dns
340 */
341 static int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx,
342                              const struct ldb_val *v1, const struct ldb_val *v2)
343 {
344         struct ldb_dn *dn1 = NULL, *dn2 = NULL;
345         int ret;
346
347         dn1 = ldb_dn_from_ldb_val(mem_ctx, ldb, v1);
348         if ( ! ldb_dn_validate(dn1)) return -1;
349
350         dn2 = ldb_dn_from_ldb_val(mem_ctx, ldb, v2);
351         if ( ! ldb_dn_validate(dn2)) {
352                 talloc_free(dn1);
353                 return -1;
354         } 
355
356         ret = ldb_dn_compare(dn1, dn2);
357
358         talloc_free(dn1);
359         talloc_free(dn2);
360         return ret;
361 }
362
363 /*
364   compare two utc time values. 1 second resolution
365 */
366 static int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx,
367                                   const struct ldb_val *v1, const struct ldb_val *v2)
368 {
369         time_t t1=0, t2=0;
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;
374 }
375
376 /*
377   canonicalise a utc time
378 */
379 static int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx,
380                                     const struct ldb_val *in, struct ldb_val *out)
381 {
382         time_t t;
383         int ret;
384         ret = ldb_val_to_time(in, &t);
385         if (ret != LDB_SUCCESS) {
386                 return ret;
387         }
388         out->data = (uint8_t *)ldb_timestring(mem_ctx, t);
389         if (out->data == NULL) {
390                 ldb_oom(ldb);
391                 return LDB_ERR_OPERATIONS_ERROR;
392         }
393         out->length = strlen((char *)out->data);
394         return 0;
395 }
396
397 /*
398   table of standard attribute handlers
399 */
400 static const struct ldb_schema_syntax ldb_standard_syntaxes[] = {
401         { 
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
407         },
408         { 
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
414         },
415         { 
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
421         },
422         { 
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
428         },
429         { 
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
435         },
436         { 
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
442         },
443         { 
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
449         },
450 };
451
452
453 /*
454   return the attribute handlers for a given syntax name
455 */
456 const struct ldb_schema_syntax *ldb_standard_syntax_by_name(struct ldb_context *ldb,
457                                                             const char *syntax)
458 {
459         unsigned int i;
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];
465                 }
466         }
467         return NULL;
468 }
469
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)
474 {
475         int ret, ret1, ret2;
476         struct ldb_val v1_canon, v2_canon;
477         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
478
479         /* I could try and bail if tmp_ctx was NULL, but what return
480          * value would I use?
481          *
482          * It seems easier to continue on the NULL context 
483          */
484         ret1 = canonicalise_fn(ldb, tmp_ctx, v1, &v1_canon);
485         ret2 = canonicalise_fn(ldb, tmp_ctx, v2, &v2_canon);
486
487         if (ret1 == LDB_SUCCESS && ret2 == LDB_SUCCESS) {
488                 ret = ldb_comparison_binary(ldb, mem_ctx, &v1_canon, &v2_canon);
489         } else {
490                 ret = ldb_comparison_binary(ldb, mem_ctx, v1, v2);
491         }
492         talloc_free(tmp_ctx);
493         return ret;
494 }