make sure we never look past the end of either string in ldb_comparison_fold()
[samba.git] / source4 / lib / ldb / common / attrib_handlers.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Tridgell  2005
5
6      ** NOTE! The following LGPL license applies to the ldb
7      ** library. This does NOT imply that all of Samba is released
8      ** under the LGPL
9    
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 3 of the License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Lesser General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, see <http://www.gnu.org/licenses/>.
22 */
23 /*
24   attribute handlers for well known attribute types, selected by syntax OID
25   see rfc2252
26 */
27
28 #include "ldb_private.h"
29 #include "system/locale.h"
30 #include "ldb_handlers.h"
31
32 /*
33   default handler that just copies a ldb_val.
34 */
35 int ldb_handler_copy(struct ldb_context *ldb, void *mem_ctx,
36                      const struct ldb_val *in, struct ldb_val *out)
37 {
38         *out = ldb_val_dup(mem_ctx, in);
39         if (in->length > 0 && out->data == NULL) {
40                 ldb_oom(ldb);
41                 return -1;
42         }
43         return 0;
44 }
45
46 /*
47   a case folding copy handler, removing leading and trailing spaces and
48   multiple internal spaces
49
50   We exploit the fact that utf8 never uses the space octet except for
51   the space itself
52 */
53 int ldb_handler_fold(struct ldb_context *ldb, void *mem_ctx,
54                             const struct ldb_val *in, struct ldb_val *out)
55 {
56         char *s, *t;
57         int l;
58
59         if (!in || !out || !(in->data)) {
60                 return -1;
61         }
62
63         out->data = (uint8_t *)ldb_casefold(ldb, mem_ctx, (const char *)(in->data), in->length);
64         if (out->data == NULL) {
65                 ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_handler_fold: unable to casefold string [%s]", in->data);
66                 return -1;
67         }
68
69         s = (char *)(out->data);
70         
71         /* remove trailing spaces if any */
72         l = strlen(s);
73         while (l > 0 && s[l - 1] == ' ') l--;
74         s[l] = '\0';
75         
76         /* remove leading spaces if any */
77         if (*s == ' ') {
78                 for (t = s; *s == ' '; s++) ;
79
80                 /* remove leading spaces by moving down the string */
81                 memmove(t, s, l);
82
83                 s = t;
84         }
85
86         /* check middle spaces */
87         while ((t = strchr(s, ' ')) != NULL) {
88                 for (s = t; *s == ' '; s++) ;
89
90                 if ((s - t) > 1) {
91                         l = strlen(s);
92
93                         /* remove all spaces but one by moving down the string */
94                         memmove(t + 1, s, l);
95                 }
96         }
97
98         out->length = strlen((char *)out->data);
99         return 0;
100 }
101
102
103
104 /*
105   canonicalise a ldap Integer
106   rfc2252 specifies it should be in decimal form
107 */
108 static int ldb_canonicalise_Integer(struct ldb_context *ldb, void *mem_ctx,
109                                     const struct ldb_val *in, struct ldb_val *out)
110 {
111         char *end;
112         long long i = strtoll((char *)in->data, &end, 0);
113         if (*end != 0) {
114                 return -1;
115         }
116         out->data = (uint8_t *)talloc_asprintf(mem_ctx, "%lld", i);
117         if (out->data == NULL) {
118                 return -1;
119         }
120         out->length = strlen((char *)out->data);
121         return 0;
122 }
123
124 /*
125   compare two Integers
126 */
127 static int ldb_comparison_Integer(struct ldb_context *ldb, void *mem_ctx,
128                                   const struct ldb_val *v1, const struct ldb_val *v2)
129 {
130         return strtoll((char *)v1->data, NULL, 0) - strtoll((char *)v2->data, NULL, 0);
131 }
132
133 /*
134   canonicalise a ldap Boolean
135   rfc2252 specifies it should be either "TRUE" or "FALSE"
136 */
137 static int ldb_canonicalise_Boolean(struct ldb_context *ldb, void *mem_ctx,
138                              const struct ldb_val *in, struct ldb_val *out)
139 {
140         if (strncasecmp((char *)in->data, "TRUE", in->length) == 0) {
141                 out->data = (uint8_t *)talloc_strdup(mem_ctx, "TRUE");
142                 out->length = 4;
143         } else if (strncasecmp((char *)in->data, "FALSE", in->length) == 0) {
144                 out->data = (uint8_t *)talloc_strdup(mem_ctx, "FALSE");
145                 out->length = 4;
146         } else {
147                 return -1;
148         }
149         return 0;
150 }
151
152 /*
153   compare two Booleans
154 */
155 static int ldb_comparison_Boolean(struct ldb_context *ldb, void *mem_ctx,
156                            const struct ldb_val *v1, const struct ldb_val *v2)
157 {
158         if (v1->length != v2->length) {
159                 return v1->length - v2->length;
160         }
161         return strncasecmp((char *)v1->data, (char *)v2->data, v1->length);
162 }
163
164
165 /*
166   compare two binary blobs
167 */
168 int ldb_comparison_binary(struct ldb_context *ldb, void *mem_ctx,
169                           const struct ldb_val *v1, const struct ldb_val *v2)
170 {
171         if (v1->length != v2->length) {
172                 return v1->length - v2->length;
173         }
174         return memcmp(v1->data, v2->data, v1->length);
175 }
176
177 /*
178   compare two case insensitive strings, ignoring multiple whitespaces
179   and leading and trailing whitespaces
180   see rfc2252 section 8.1
181         
182   try to optimize for the ascii case,
183   but if we find out an utf8 codepoint revert to slower but correct function
184 */
185 int ldb_comparison_fold(struct ldb_context *ldb, void *mem_ctx,
186                                const struct ldb_val *v1, const struct ldb_val *v2)
187 {
188         const char *s1=(const char *)v1->data, *s2=(const char *)v2->data;
189         size_t n1 = v1->length, n2 = v2->length;
190         char *b1, *b2;
191         const char *u1, *u2;
192         int ret;
193         while (n1 && *s1 == ' ') { s1++; n1--; };
194         while (n2 && *s2 == ' ') { s2++; n2--; };
195
196         while (n1 && n2 && *s1 && *s2) {
197                 /* the first 127 (0x7F) chars are ascii and utf8 guarantes they
198                  * never appear in multibyte sequences */
199                 if (((unsigned char)s1[0]) & 0x80) goto utf8str;
200                 if (((unsigned char)s2[0]) & 0x80) goto utf8str;
201                 if (toupper((unsigned char)*s1) != toupper((unsigned char)*s2))
202                         break;
203                 if (*s1 == ' ') {
204                         while (n1 && s1[0] == s1[1]) { s1++; n1--; }
205                         while (n2 && s2[0] == s2[1]) { s2++; n2--; }
206                 }
207                 s1++; s2++;
208                 n1--; n2--;
209         }
210
211         /* check for trailing spaces only if the other pointers has
212          * reached the end of the strings otherwise we can
213          * mistakenly match.  ex. "domain users" <->
214          * "domainUpdates"
215          */
216         if (n1 && *s1 == ' ' && (!n2 || !*s2)) {
217                 while (n1 && *s1 == ' ') { s1++; n1--; }                
218         }
219         if (n2 && *s2 == ' ' && (!n1 || !*s1)) {
220                 while (n2 && *s2 == ' ') { s2++; n2--; }                
221         }
222         if (n1 == 0 && n2 != 0) {
223                 return -(int)toupper(*s2);
224         }
225         if (n2 == 0 && n1 != 0) {
226                 return (int)toupper(*s1);
227         }
228         if (n2 == 0 && n2 == 0) {
229                 return 0;
230         }
231         return (int)toupper(*s1) - (int)toupper(*s2);
232
233 utf8str:
234         /* no need to recheck from the start, just from the first utf8 char found */
235         b1 = ldb_casefold(ldb, mem_ctx, s1, n1);
236         b2 = ldb_casefold(ldb, mem_ctx, s2, n2);
237
238         if (!b1 || !b2) {
239                 /* One of the strings was not UTF8, so we have no
240                  * options but to do a binary compare */
241                 talloc_free(b1);
242                 talloc_free(b2);
243                 if (memcmp(s1, s2, MIN(n1, n2)) == 0) {
244                         if (n1 == n2) return 0;
245                         if (n1 > n2) {
246                                 return (int)toupper(s1[n2]);
247                         } else {
248                                 return -(int)toupper(s2[n1]);
249                         }
250                 }
251         }
252
253         u1 = b1;
254         u2 = b2;
255
256         while (*u1 & *u2) {
257                 if (*u1 != *u2)
258                         break;
259                 if (*u1 == ' ') {
260                         while (u1[0] == u1[1]) u1++;
261                         while (u2[0] == u2[1]) u2++;
262                 }
263                 u1++; u2++;
264         }
265         if (! (*u1 && *u2)) {
266                 while (*u1 == ' ') u1++;
267                 while (*u2 == ' ') u2++;
268         }
269         ret = (int)(*u1 - *u2);
270
271         talloc_free(b1);
272         talloc_free(b2);
273         
274         return ret;
275 }
276
277
278 /*
279   canonicalise a attribute in DN format
280 */
281 static int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx,
282                                const struct ldb_val *in, struct ldb_val *out)
283 {
284         struct ldb_dn *dn;
285         int ret = -1;
286
287         out->length = 0;
288         out->data = NULL;
289
290         dn = ldb_dn_from_ldb_val(ldb, mem_ctx, in);
291         if ( ! ldb_dn_validate(dn)) {
292                 return LDB_ERR_INVALID_DN_SYNTAX;
293         }
294
295         out->data = (uint8_t *)ldb_dn_alloc_casefold(mem_ctx, dn);
296         if (out->data == NULL) {
297                 goto done;
298         }
299         out->length = strlen((char *)out->data);
300
301         ret = 0;
302
303 done:
304         talloc_free(dn);
305
306         return ret;
307 }
308
309 /*
310   compare two dns
311 */
312 static int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx,
313                              const struct ldb_val *v1, const struct ldb_val *v2)
314 {
315         struct ldb_dn *dn1 = NULL, *dn2 = NULL;
316         int ret;
317
318         dn1 = ldb_dn_from_ldb_val(ldb, mem_ctx, v1);
319         if ( ! ldb_dn_validate(dn1)) return -1;
320
321         dn2 = ldb_dn_from_ldb_val(ldb, mem_ctx, v2);
322         if ( ! ldb_dn_validate(dn2)) {
323                 talloc_free(dn1);
324                 return -1;
325         } 
326
327         ret = ldb_dn_compare(dn1, dn2);
328
329         talloc_free(dn1);
330         talloc_free(dn2);
331         return ret;
332 }
333
334 /*
335   compare two utc time values. 1 second resolution
336 */
337 static int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx,
338                                   const struct ldb_val *v1, const struct ldb_val *v2)
339 {
340         time_t t1, t2;
341         t1 = ldb_string_to_time((char *)v1->data);
342         t2 = ldb_string_to_time((char *)v2->data);
343         return (int)t2 - (int)t1;
344 }
345
346 /*
347   canonicalise a utc time
348 */
349 static int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx,
350                                     const struct ldb_val *in, struct ldb_val *out)
351 {
352         time_t t = ldb_string_to_time((char *)in->data);
353         out->data = (uint8_t *)ldb_timestring(mem_ctx, t);
354         if (out->data == NULL) {
355                 return -1;
356         }
357         out->length = strlen((char *)out->data);
358         return 0;
359 }
360
361 /*
362   table of standard attribute handlers
363 */
364 static const struct ldb_schema_syntax ldb_standard_syntaxes[] = {
365         { 
366                 .name            = LDB_SYNTAX_INTEGER,
367                 .ldif_read_fn    = ldb_handler_copy,
368                 .ldif_write_fn   = ldb_handler_copy,
369                 .canonicalise_fn = ldb_canonicalise_Integer,
370                 .comparison_fn   = ldb_comparison_Integer
371         },
372         { 
373                 .name            = LDB_SYNTAX_OCTET_STRING,
374                 .ldif_read_fn    = ldb_handler_copy,
375                 .ldif_write_fn   = ldb_handler_copy,
376                 .canonicalise_fn = ldb_handler_copy,
377                 .comparison_fn   = ldb_comparison_binary
378         },
379         { 
380                 .name            = LDB_SYNTAX_DIRECTORY_STRING,
381                 .ldif_read_fn    = ldb_handler_copy,
382                 .ldif_write_fn   = ldb_handler_copy,
383                 .canonicalise_fn = ldb_handler_fold,
384                 .comparison_fn   = ldb_comparison_fold
385         },
386         { 
387                 .name            = LDB_SYNTAX_DN,
388                 .ldif_read_fn    = ldb_handler_copy,
389                 .ldif_write_fn   = ldb_handler_copy,
390                 .canonicalise_fn = ldb_canonicalise_dn,
391                 .comparison_fn   = ldb_comparison_dn
392         },
393         { 
394                 .name            = LDB_SYNTAX_OBJECTCLASS,
395                 .ldif_read_fn    = ldb_handler_copy,
396                 .ldif_write_fn   = ldb_handler_copy,
397                 .canonicalise_fn = ldb_handler_fold,
398                 .comparison_fn   = ldb_comparison_fold
399         },
400         { 
401                 .name            = LDB_SYNTAX_UTC_TIME,
402                 .ldif_read_fn    = ldb_handler_copy,
403                 .ldif_write_fn   = ldb_handler_copy,
404                 .canonicalise_fn = ldb_canonicalise_utctime,
405                 .comparison_fn   = ldb_comparison_utctime
406         },
407         { 
408                 .name            = LDB_SYNTAX_BOOLEAN,
409                 .ldif_read_fn    = ldb_handler_copy,
410                 .ldif_write_fn   = ldb_handler_copy,
411                 .canonicalise_fn = ldb_canonicalise_Boolean,
412                 .comparison_fn   = ldb_comparison_Boolean
413         },
414 };
415
416
417 /*
418   return the attribute handlers for a given syntax name
419 */
420 const struct ldb_schema_syntax *ldb_standard_syntax_by_name(struct ldb_context *ldb,
421                                                             const char *syntax)
422 {
423         int i;
424         unsigned num_handlers = sizeof(ldb_standard_syntaxes)/sizeof(ldb_standard_syntaxes[0]);
425         /* TODO: should be replaced with a binary search */
426         for (i=0;i<num_handlers;i++) {
427                 if (strcmp(ldb_standard_syntaxes[i].name, syntax) == 0) {
428                         return &ldb_standard_syntaxes[i];
429                 }
430         }
431         return NULL;
432 }