4869e3289c88e6f082d057565454ef0d0be192ad
[abartlet/samba.git/.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         const char *u1, *u2;
191         char *b1, *b2;
192         int ret;
193         while (*s1 == ' ' && n1) { s1++; n1--; };
194         while (*s2 == ' ' && n2) { s2++; n2--; };
195         /* TODO: make utf8 safe, possibly with helper function from application */
196         while (*s1 && *s2 && n1 && n2) {
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 (s1[0] == s1[1] && n1) { s1++; n1--; }
205                         while (s2[0] == s2[1] && n2) { s2++; n2--; }
206                 }
207                 s1++; s2++;
208                 n1--; n2--;
209         }
210         if (! (*s1 && *s2)) {
211                 /* check for trailing spaces only if one of the pointers
212                  * has reached the end of the strings otherwise we
213                  * can mistakenly match.
214                  * ex. "domain users" <-> "domainUpdates"
215                  */
216                 while (*s1 == ' ') { s1++; n1--; }
217                 while (*s2 == ' ') { s2++; n2--; }
218         }
219         if (n1 != n2) {
220                 return n1 - n2;
221         }
222         return (int)(toupper(*s1)) - (int)(toupper(*s2));
223
224 utf8str:
225         /* no need to recheck from the start, just from the first utf8 char found */
226         b1 = ldb_casefold(ldb, mem_ctx, s1, n1);
227         b2 = ldb_casefold(ldb, mem_ctx, s2, n2);
228
229         if (b1 && b2) {
230                 /* Both strings converted correctly */
231
232                 u1 = b1;
233                 u2 = b2;
234         } else {
235                 /* One of the strings was not UTF8, so we have no options but to do a binary compare */
236
237                 u1 = s1;
238                 u2 = s2;
239         }
240
241         while (*u1 & *u2) {
242                 if (*u1 != *u2)
243                         break;
244                 if (*u1 == ' ') {
245                         while (u1[0] == u1[1]) u1++;
246                         while (u2[0] == u2[1]) u2++;
247                 }
248                 u1++; u2++;
249         }
250         if (! (*u1 && *u2)) {
251                 while (*u1 == ' ') u1++;
252                 while (*u2 == ' ') u2++;
253         }
254         ret = (int)(*u1 - *u2);
255
256         talloc_free(b1);
257         talloc_free(b2);
258         
259         return ret;
260 }
261
262
263 /*
264   canonicalise a attribute in DN format
265 */
266 static int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx,
267                                const struct ldb_val *in, struct ldb_val *out)
268 {
269         struct ldb_dn *dn;
270         int ret = -1;
271
272         out->length = 0;
273         out->data = NULL;
274
275         dn = ldb_dn_from_ldb_val(ldb, mem_ctx, in);
276         if ( ! ldb_dn_validate(dn)) {
277                 return LDB_ERR_INVALID_DN_SYNTAX;
278         }
279
280         out->data = (uint8_t *)ldb_dn_alloc_casefold(mem_ctx, dn);
281         if (out->data == NULL) {
282                 goto done;
283         }
284         out->length = strlen((char *)out->data);
285
286         ret = 0;
287
288 done:
289         talloc_free(dn);
290
291         return ret;
292 }
293
294 /*
295   compare two dns
296 */
297 static int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx,
298                              const struct ldb_val *v1, const struct ldb_val *v2)
299 {
300         struct ldb_dn *dn1 = NULL, *dn2 = NULL;
301         int ret;
302
303         dn1 = ldb_dn_from_ldb_val(ldb, mem_ctx, v1);
304         if ( ! ldb_dn_validate(dn1)) return -1;
305
306         dn2 = ldb_dn_from_ldb_val(ldb, mem_ctx, v2);
307         if ( ! ldb_dn_validate(dn2)) {
308                 talloc_free(dn1);
309                 return -1;
310         } 
311
312         ret = ldb_dn_compare(dn1, dn2);
313
314         talloc_free(dn1);
315         talloc_free(dn2);
316         return ret;
317 }
318
319 /*
320   compare two utc time values. 1 second resolution
321 */
322 static int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx,
323                                   const struct ldb_val *v1, const struct ldb_val *v2)
324 {
325         time_t t1, t2;
326         t1 = ldb_string_to_time((char *)v1->data);
327         t2 = ldb_string_to_time((char *)v2->data);
328         return (int)t2 - (int)t1;
329 }
330
331 /*
332   canonicalise a utc time
333 */
334 static int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx,
335                                     const struct ldb_val *in, struct ldb_val *out)
336 {
337         time_t t = ldb_string_to_time((char *)in->data);
338         out->data = (uint8_t *)ldb_timestring(mem_ctx, t);
339         if (out->data == NULL) {
340                 return -1;
341         }
342         out->length = strlen((char *)out->data);
343         return 0;
344 }
345
346 /*
347   table of standard attribute handlers
348 */
349 static const struct ldb_schema_syntax ldb_standard_syntaxes[] = {
350         { 
351                 .name            = LDB_SYNTAX_INTEGER,
352                 .ldif_read_fn    = ldb_handler_copy,
353                 .ldif_write_fn   = ldb_handler_copy,
354                 .canonicalise_fn = ldb_canonicalise_Integer,
355                 .comparison_fn   = ldb_comparison_Integer
356         },
357         { 
358                 .name            = LDB_SYNTAX_OCTET_STRING,
359                 .ldif_read_fn    = ldb_handler_copy,
360                 .ldif_write_fn   = ldb_handler_copy,
361                 .canonicalise_fn = ldb_handler_copy,
362                 .comparison_fn   = ldb_comparison_binary
363         },
364         { 
365                 .name            = LDB_SYNTAX_DIRECTORY_STRING,
366                 .ldif_read_fn    = ldb_handler_copy,
367                 .ldif_write_fn   = ldb_handler_copy,
368                 .canonicalise_fn = ldb_handler_fold,
369                 .comparison_fn   = ldb_comparison_fold
370         },
371         { 
372                 .name            = LDB_SYNTAX_DN,
373                 .ldif_read_fn    = ldb_handler_copy,
374                 .ldif_write_fn   = ldb_handler_copy,
375                 .canonicalise_fn = ldb_canonicalise_dn,
376                 .comparison_fn   = ldb_comparison_dn
377         },
378         { 
379                 .name            = LDB_SYNTAX_OBJECTCLASS,
380                 .ldif_read_fn    = ldb_handler_copy,
381                 .ldif_write_fn   = ldb_handler_copy,
382                 .canonicalise_fn = ldb_handler_fold,
383                 .comparison_fn   = ldb_comparison_fold
384         },
385         { 
386                 .name            = LDB_SYNTAX_UTC_TIME,
387                 .ldif_read_fn    = ldb_handler_copy,
388                 .ldif_write_fn   = ldb_handler_copy,
389                 .canonicalise_fn = ldb_canonicalise_utctime,
390                 .comparison_fn   = ldb_comparison_utctime
391         },
392         { 
393                 .name            = LDB_SYNTAX_BOOLEAN,
394                 .ldif_read_fn    = ldb_handler_copy,
395                 .ldif_write_fn   = ldb_handler_copy,
396                 .canonicalise_fn = ldb_canonicalise_Boolean,
397                 .comparison_fn   = ldb_comparison_Boolean
398         },
399 };
400
401
402 /*
403   return the attribute handlers for a given syntax name
404 */
405 const struct ldb_schema_syntax *ldb_standard_syntax_by_name(struct ldb_context *ldb,
406                                                             const char *syntax)
407 {
408         int i;
409         unsigned num_handlers = sizeof(ldb_standard_syntaxes)/sizeof(ldb_standard_syntaxes[0]);
410         /* TODO: should be replaced with a binary search */
411         for (i=0;i<num_handlers;i++) {
412                 if (strcmp(ldb_standard_syntaxes[i].name, syntax) == 0) {
413                         return &ldb_standard_syntaxes[i];
414                 }
415         }
416         return NULL;
417 }