80725ec04f9c5966a08964ac5e641b403c4ba2c9
[ira/wip.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 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 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   compare two binary blobs
135 */
136 int ldb_comparison_binary(struct ldb_context *ldb, void *mem_ctx,
137                           const struct ldb_val *v1, const struct ldb_val *v2)
138 {
139         if (v1->length != v2->length) {
140                 return v1->length - v2->length;
141         }
142         return memcmp(v1->data, v2->data, v1->length);
143 }
144
145 /*
146   compare two case insensitive strings, ignoring multiple whitespaces
147   and leading and trailing whitespaces
148   see rfc2252 section 8.1
149         
150   try to optimize for the ascii case,
151   but if we find out an utf8 codepoint revert to slower but correct function
152 */
153 int ldb_comparison_fold(struct ldb_context *ldb, void *mem_ctx,
154                                const struct ldb_val *v1, const struct ldb_val *v2)
155 {
156         const char *s1=(const char *)v1->data, *s2=(const char *)v2->data;
157         size_t n1 = v1->length, n2 = v2->length;
158         const char *u1, *u2;
159         char *b1, *b2;
160         int ret;
161         while (*s1 == ' ' && n1) { s1++; n1--; };
162         while (*s2 == ' ' && n2) { s2++; n2--; };
163         /* TODO: make utf8 safe, possibly with helper function from application */
164         while (*s1 && *s2 && n1 && n2) {
165                 /* the first 127 (0x7F) chars are ascii and utf8 guarantes they
166                  * never appear in multibyte sequences */
167                 if (((unsigned char)s1[0]) & 0x80) goto utf8str;
168                 if (((unsigned char)s2[0]) & 0x80) goto utf8str;
169                 if (toupper((unsigned char)*s1) != toupper((unsigned char)*s2))
170                         break;
171                 if (*s1 == ' ') {
172                         while (s1[0] == s1[1] && n1) { s1++; n1--; }
173                         while (s2[0] == s2[1] && n2) { s2++; n2--; }
174                 }
175                 s1++; s2++;
176                 n1--; n2--;
177         }
178         if (! (*s1 && *s2)) {
179                 /* check for trailing spaces only if one of the pointers
180                  * has reached the end of the strings otherwise we
181                  * can mistakenly match.
182                  * ex. "domain users" <-> "domainUpdates"
183                  */
184                 while (*s1 == ' ') { s1++; n1--; }
185                 while (*s2 == ' ') { s2++; n2--; }
186         }
187         if (n1 != n2) {
188                 return n1 - n2;
189         }
190         return (int)(toupper(*s1)) - (int)(toupper(*s2));
191
192 utf8str:
193         /* no need to recheck from the start, just from the first utf8 char found */
194         b1 = ldb_casefold(ldb, mem_ctx, s1, n1);
195         b2 = ldb_casefold(ldb, mem_ctx, s2, n2);
196
197         if (b1 && b2) {
198                 /* Both strings converted correctly */
199
200                 u1 = b1;
201                 u2 = b2;
202         } else {
203                 /* One of the strings was not UTF8, so we have no options but to do a binary compare */
204
205                 u1 = s1;
206                 u2 = s2;
207         }
208
209         while (*u1 & *u2) {
210                 if (*u1 != *u2)
211                         break;
212                 if (*u1 == ' ') {
213                         while (u1[0] == u1[1]) u1++;
214                         while (u2[0] == u2[1]) u2++;
215                 }
216                 u1++; u2++;
217         }
218         if (! (*u1 && *u2)) {
219                 while (*u1 == ' ') u1++;
220                 while (*u2 == ' ') u2++;
221         }
222         ret = (int)(*u1 - *u2);
223
224         talloc_free(b1);
225         talloc_free(b2);
226         
227         return ret;
228 }
229
230
231 /*
232   canonicalise a attribute in DN format
233 */
234 int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx,
235                                const struct ldb_val *in, struct ldb_val *out)
236 {
237         struct ldb_dn *dn;
238         int ret = -1;
239
240         out->length = 0;
241         out->data = NULL;
242
243         dn = ldb_dn_from_ldb_val(ldb, mem_ctx, in);
244         if ( ! ldb_dn_validate(dn)) {
245                 return LDB_ERR_INVALID_DN_SYNTAX;
246         }
247
248         out->data = (uint8_t *)ldb_dn_alloc_casefold(mem_ctx, dn);
249         if (out->data == NULL) {
250                 goto done;
251         }
252         out->length = strlen((char *)out->data);
253
254         ret = 0;
255
256 done:
257         talloc_free(dn);
258
259         return ret;
260 }
261
262 /*
263   compare two dns
264 */
265 int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx,
266                              const struct ldb_val *v1, const struct ldb_val *v2)
267 {
268         struct ldb_dn *dn1 = NULL, *dn2 = NULL;
269         int ret;
270
271         dn1 = ldb_dn_from_ldb_val(ldb, mem_ctx, v1);
272         if ( ! ldb_dn_validate(dn1)) return -1;
273
274         dn2 = ldb_dn_from_ldb_val(ldb, mem_ctx, v2);
275         if ( ! ldb_dn_validate(dn2)) {
276                 talloc_free(dn1);
277                 return -1;
278         } 
279
280         ret = ldb_dn_compare(dn1, dn2);
281
282         talloc_free(dn1);
283         talloc_free(dn2);
284         return ret;
285 }
286
287 /*
288   compare two utc time values. 1 second resolution
289 */
290 int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx,
291                                   const struct ldb_val *v1, const struct ldb_val *v2)
292 {
293         time_t t1, t2;
294         t1 = ldb_string_to_time((char *)v1->data);
295         t2 = ldb_string_to_time((char *)v2->data);
296         return (int)t2 - (int)t1;
297 }
298
299 /*
300   canonicalise a utc time
301 */
302 int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx,
303                                     const struct ldb_val *in, struct ldb_val *out)
304 {
305         time_t t = ldb_string_to_time((char *)in->data);
306         out->data = (uint8_t *)ldb_timestring(mem_ctx, t);
307         if (out->data == NULL) {
308                 return -1;
309         }
310         out->length = strlen((char *)out->data);
311         return 0;
312 }
313
314 /*
315   table of standard attribute handlers
316 */
317 static const struct ldb_schema_syntax ldb_standard_syntaxes[] = {
318         { 
319                 .name            = LDB_SYNTAX_INTEGER,
320                 .ldif_read_fn    = ldb_handler_copy,
321                 .ldif_write_fn   = ldb_handler_copy,
322                 .canonicalise_fn = ldb_canonicalise_Integer,
323                 .comparison_fn   = ldb_comparison_Integer
324         },
325         { 
326                 .name            = LDB_SYNTAX_OCTET_STRING,
327                 .ldif_read_fn    = ldb_handler_copy,
328                 .ldif_write_fn   = ldb_handler_copy,
329                 .canonicalise_fn = ldb_handler_copy,
330                 .comparison_fn   = ldb_comparison_binary
331         },
332         { 
333                 .name            = LDB_SYNTAX_DIRECTORY_STRING,
334                 .ldif_read_fn    = ldb_handler_copy,
335                 .ldif_write_fn   = ldb_handler_copy,
336                 .canonicalise_fn = ldb_handler_fold,
337                 .comparison_fn   = ldb_comparison_fold
338         },
339         { 
340                 .name            = LDB_SYNTAX_DN,
341                 .ldif_read_fn    = ldb_handler_copy,
342                 .ldif_write_fn   = ldb_handler_copy,
343                 .canonicalise_fn = ldb_canonicalise_dn,
344                 .comparison_fn   = ldb_comparison_dn
345         },
346         { 
347                 .name            = LDB_SYNTAX_OBJECTCLASS,
348                 .ldif_read_fn    = ldb_handler_copy,
349                 .ldif_write_fn   = ldb_handler_copy,
350                 .canonicalise_fn = ldb_handler_fold,
351                 .comparison_fn   = ldb_comparison_fold
352         },
353         { 
354                 .name            = LDB_SYNTAX_UTC_TIME,
355                 .ldif_read_fn    = ldb_handler_copy,
356                 .ldif_write_fn   = ldb_handler_copy,
357                 .canonicalise_fn = ldb_canonicalise_utctime,
358                 .comparison_fn   = ldb_comparison_utctime
359         }
360 };
361
362
363 /*
364   return the attribute handlers for a given syntax name
365 */
366 const struct ldb_schema_syntax *ldb_standard_syntax_by_name(struct ldb_context *ldb,
367                                                             const char *syntax)
368 {
369         int i;
370         unsigned num_handlers = sizeof(ldb_standard_syntaxes)/sizeof(ldb_standard_syntaxes[0]);
371         /* TODO: should be replaced with a binary search */
372         for (i=0;i<num_handlers;i++) {
373                 if (strcmp(ldb_standard_syntaxes[i].name, syntax) == 0) {
374                         return &ldb_standard_syntaxes[i];
375                 }
376         }
377         return NULL;
378 }