r13347: - Now we compare values with an optimized utf8
[metze/samba/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 2 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, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 */
24 /*
25   attribute handlers for well known attribute types, selected by syntax OID
26   see rfc2252
27 */
28
29 #include "includes.h"
30 #include "ldb/include/includes.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 static 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         if (!in || !out || !(in->data)) {
59                 return -1;
60         }
61
62         out->data = (uint8_t *)ldb_casefold(ldb, mem_ctx, (const char *)(in->data));
63         if (out->data == NULL) {
64                 ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_handler_fold: unable to casefold string [%s]", in->data);
65                 return -1;
66         }
67
68         s = (char *)(out->data);
69         
70         /* remove trailing spaces if any */
71         l = strlen(s);
72         while (s[l - 1] == ' ') l--;
73         s[l] = '\0';
74         
75         /* remove leading spaces if any */
76         if (*s == ' ') {
77                 for (t = s; *s == ' '; s++) ;
78
79                 /* remove leading spaces by moving down the string */
80                 memmove(t, s, l);
81
82                 s = t;
83         }
84
85         /* check middle spaces */
86         while ((t = strchr(s, ' ')) != NULL) {
87                 for (s = t; *s == ' '; s++) ;
88
89                 if ((s - t) > 1) {
90                         l = strlen(s);
91
92                         /* remove all spaces but one by moving down the string */
93                         memmove(t + 1, s, l);
94                 }
95         }
96
97         out->length = strlen((char *)out->data);
98         return 0;
99 }
100
101
102
103 /*
104   canonicalise a ldap Integer
105   rfc2252 specifies it should be in decimal form
106 */
107 static int ldb_canonicalise_Integer(struct ldb_context *ldb, void *mem_ctx,
108                                     const struct ldb_val *in, struct ldb_val *out)
109 {
110         char *end;
111         long long i = strtoll((char *)in->data, &end, 0);
112         if (*end != 0) {
113                 return -1;
114         }
115         out->data = (uint8_t *)talloc_asprintf(mem_ctx, "%lld", i);
116         if (out->data == NULL) {
117                 return -1;
118         }
119         out->length = strlen((char *)out->data);
120         return 0;
121 }
122
123 /*
124   compare two Integers
125 */
126 static int ldb_comparison_Integer(struct ldb_context *ldb, void *mem_ctx,
127                                   const struct ldb_val *v1, const struct ldb_val *v2)
128 {
129         return strtoll((char *)v1->data, NULL, 0) - strtoll((char *)v2->data, NULL, 0);
130 }
131
132 /*
133   compare two binary blobs
134 */
135 int ldb_comparison_binary(struct ldb_context *ldb, void *mem_ctx,
136                           const struct ldb_val *v1, const struct ldb_val *v2)
137 {
138         if (v1->length != v2->length) {
139                 return v1->length - v2->length;
140         }
141         return memcmp(v1->data, v2->data, v1->length);
142 }
143
144 /*
145   compare two case insensitive strings, ignoring multiple whitespaces
146   and leading and trailing whitespaces
147   see rfc2252 section 8.1
148         
149   try to optimize for the ascii case,
150   but if we find out an utf8 codepoint revert to slower but correct function
151 */
152 static int ldb_comparison_fold(struct ldb_context *ldb, void *mem_ctx,
153                                const struct ldb_val *v1, const struct ldb_val *v2)
154 {
155         const char *s1=(const char *)v1->data, *s2=(const char *)v2->data;
156         char *b1, *b2, *u1, *u2;
157         int ret;
158         while (*s1 == ' ') s1++;
159         while (*s2 == ' ') s2++;
160         /* TODO: make utf8 safe, possibly with helper function from application */
161         while (*s1 && *s2) {
162                 /* the first 127 (0x7F) chars are ascii and utf8 guarantes they
163                  * never appear in multibyte sequences */
164                 if (((unsigned char)s1[0]) & 0x80) goto utf8str;
165                 if (((unsigned char)s2[0]) & 0x80) goto utf8str;
166                 if (toupper((unsigned char)*s1) != toupper((unsigned char)*s2))
167                         break;
168                 if (*s1 == ' ') {
169                         while (s1[0] == s1[1]) s1++;
170                         while (s2[0] == s2[1]) s2++;
171                 }
172                 s1++; s2++;
173         }
174         if (! (*s1 && *s2)) {
175                 /* check for trailing spaces only if one of the pointers
176                  * has reached the end of the strings otherwise we
177                  * can mistakenly match.
178                  * ex. "domain users" <-> "domainUpdates"
179                  */
180                 while (*s1 == ' ') s1++;
181                 while (*s2 == ' ') s2++;
182         }
183         return (int)(toupper(*s1)) - (int)(toupper(*s2));
184
185 utf8str:
186         /* non need to recheck from the start, just from the first utf8 char found */
187         b1 = u1 = ldb_casefold(ldb, mem_ctx, s1);
188         b2 = u2 = ldb_casefold(ldb, mem_ctx, s2);
189         
190         while (*u1 & *u2) {
191                 if (*u1 != *u2)
192                         break;
193                 if (*u1 == ' ') {
194                         while (u1[0] == u1[1]) u1++;
195                         while (u2[0] == u2[1]) u2++;
196                 }
197                 u1++; u2++;
198         }
199         if (! (*u1 && *u2)) {
200                 while (*u1 == ' ') u1++;
201                 while (*u2 == ' ') u2++;
202         }
203         ret = (int)(*u1 - *u2);
204         talloc_free(b1);
205         talloc_free(b2);
206
207         return ret;
208 }
209
210 /*
211   canonicalise a attribute in DN format
212 */
213 static int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx,
214                                const struct ldb_val *in, struct ldb_val *out)
215 {
216         struct ldb_dn *dn;
217         int ret = -1;
218
219         out->length = 0;
220         out->data = NULL;
221
222         dn = ldb_dn_explode_casefold(ldb, (char *)in->data);
223         if (dn == NULL) {
224                 return -1;
225         }
226
227         out->data = (uint8_t *)ldb_dn_linearize(mem_ctx, dn);
228         if (out->data == NULL) {
229                 goto done;
230         }
231         out->length = strlen((char *)out->data);
232
233         ret = 0;
234
235 done:
236         talloc_free(dn);
237
238         return ret;
239 }
240
241 /*
242   compare two dns
243 */
244 static int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx,
245                              const struct ldb_val *v1, const struct ldb_val *v2)
246 {
247         struct ldb_dn *dn1 = NULL, *dn2 = NULL;
248         int ret;
249
250         dn1 = ldb_dn_explode_casefold(mem_ctx, (char *)v1->data);
251         if (dn1 == NULL) return -1;
252
253         dn2 = ldb_dn_explode_casefold(mem_ctx, (char *)v2->data);
254         if (dn2 == NULL) {
255                 talloc_free(dn1);
256                 return -1;
257         } 
258
259         ret = ldb_dn_compare(ldb, dn1, dn2);
260
261         talloc_free(dn1);
262         talloc_free(dn2);
263         return ret;
264 }
265
266 /*
267   compare two objectclasses, looking at subclasses
268 */
269 static int ldb_comparison_objectclass(struct ldb_context *ldb, void *mem_ctx,
270                                       const struct ldb_val *v1, const struct ldb_val *v2)
271 {
272         int ret, i;
273         const char **subclasses;
274         ret = ldb_comparison_fold(ldb, mem_ctx, v1, v2);
275         if (ret == 0) {
276                 return 0;
277         }
278         subclasses = ldb_subclass_list(ldb, (char *)v1->data);
279         if (subclasses == NULL) {
280                 return ret;
281         }
282         for (i=0;subclasses[i];i++) {
283                 struct ldb_val vs;
284                 vs.data = discard_const(subclasses[i]);
285                 vs.length = strlen(subclasses[i]);
286                 if (ldb_comparison_objectclass(ldb, mem_ctx, &vs, v2) == 0) {
287                         return 0;
288                 }
289         }
290         return ret;
291 }
292
293 /*
294   compare two utc time values. 1 second resolution
295 */
296 static int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx,
297                                   const struct ldb_val *v1, const struct ldb_val *v2)
298 {
299         time_t t1, t2;
300         t1 = ldb_string_to_time((char *)v1->data);
301         t2 = ldb_string_to_time((char *)v2->data);
302         return (int)t2 - (int)t1;
303 }
304
305 /*
306   canonicalise a utc time
307 */
308 static int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx,
309                                     const struct ldb_val *in, struct ldb_val *out)
310 {
311         time_t t = ldb_string_to_time((char *)in->data);
312         out->data = (uint8_t *)ldb_timestring(mem_ctx, t);
313         if (out->data == NULL) {
314                 return -1;
315         }
316         out->length = strlen((char *)out->data);
317         return 0;
318 }
319
320 /*
321   table of standard attribute handlers
322 */
323 static const struct ldb_attrib_handler ldb_standard_attribs[] = {
324         { 
325                 .attr            = LDB_SYNTAX_INTEGER,
326                 .flags           = 0,
327                 .ldif_read_fn    = ldb_handler_copy,
328                 .ldif_write_fn   = ldb_handler_copy,
329                 .canonicalise_fn = ldb_canonicalise_Integer,
330                 .comparison_fn   = ldb_comparison_Integer
331         },
332         { 
333                 .attr            = LDB_SYNTAX_OCTET_STRING,
334                 .flags           = 0,
335                 .ldif_read_fn    = ldb_handler_copy,
336                 .ldif_write_fn   = ldb_handler_copy,
337                 .canonicalise_fn = ldb_handler_copy,
338                 .comparison_fn   = ldb_comparison_binary
339         },
340         { 
341                 .attr            = LDB_SYNTAX_DIRECTORY_STRING,
342                 .flags           = 0,
343                 .ldif_read_fn    = ldb_handler_copy,
344                 .ldif_write_fn   = ldb_handler_copy,
345                 .canonicalise_fn = ldb_handler_fold,
346                 .comparison_fn   = ldb_comparison_fold
347         },
348         { 
349                 .attr            = LDB_SYNTAX_DN,
350                 .flags           = 0,
351                 .ldif_read_fn    = ldb_handler_copy,
352                 .ldif_write_fn   = ldb_handler_copy,
353                 .canonicalise_fn = ldb_canonicalise_dn,
354                 .comparison_fn   = ldb_comparison_dn
355         },
356         { 
357                 .attr            = LDB_SYNTAX_OBJECTCLASS,
358                 .flags           = 0,
359                 .ldif_read_fn    = ldb_handler_copy,
360                 .ldif_write_fn   = ldb_handler_copy,
361                 .canonicalise_fn = ldb_handler_fold,
362                 .comparison_fn   = ldb_comparison_objectclass
363         },
364         { 
365                 .attr            = LDB_SYNTAX_UTC_TIME,
366                 .flags           = 0,
367                 .ldif_read_fn    = ldb_handler_copy,
368                 .ldif_write_fn   = ldb_handler_copy,
369                 .canonicalise_fn = ldb_canonicalise_utctime,
370                 .comparison_fn   = ldb_comparison_utctime
371         }
372 };
373
374
375 /*
376   return the attribute handlers for a given syntax name
377 */
378 const struct ldb_attrib_handler *ldb_attrib_handler_syntax(struct ldb_context *ldb,
379                                                            const char *syntax)
380 {
381         int i;
382         unsigned num_handlers = sizeof(ldb_standard_attribs)/sizeof(ldb_standard_attribs[0]);
383         /* TODO: should be replaced with a binary search */
384         for (i=0;i<num_handlers;i++) {
385                 if (strcmp(ldb_standard_attribs[i].attr, syntax) == 0) {
386                         return &ldb_standard_attribs[i];
387                 }
388         }
389         return NULL;
390 }
391