r19832: better prototypes for the linearization functions:
[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 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 #include "system/locale.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 static 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         int l;
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));
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   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 static 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         const char *u1, *u2;
158         char *b1, *b2;
159         int ret;
160         while (*s1 == ' ') s1++;
161         while (*s2 == ' ') s2++;
162         /* TODO: make utf8 safe, possibly with helper function from application */
163         while (*s1 && *s2) {
164                 /* the first 127 (0x7F) chars are ascii and utf8 guarantes they
165                  * never appear in multibyte sequences */
166                 if (((unsigned char)s1[0]) & 0x80) goto utf8str;
167                 if (((unsigned char)s2[0]) & 0x80) goto utf8str;
168                 if (toupper((unsigned char)*s1) != toupper((unsigned char)*s2))
169                         break;
170                 if (*s1 == ' ') {
171                         while (s1[0] == s1[1]) s1++;
172                         while (s2[0] == s2[1]) s2++;
173                 }
174                 s1++; s2++;
175         }
176         if (! (*s1 && *s2)) {
177                 /* check for trailing spaces only if one of the pointers
178                  * has reached the end of the strings otherwise we
179                  * can mistakenly match.
180                  * ex. "domain users" <-> "domainUpdates"
181                  */
182                 while (*s1 == ' ') s1++;
183                 while (*s2 == ' ') s2++;
184         }
185         return (int)(toupper(*s1)) - (int)(toupper(*s2));
186
187 utf8str:
188         /* no need to recheck from the start, just from the first utf8 char found */
189         b1 = ldb_casefold(ldb, mem_ctx, s1);
190         b2 = ldb_casefold(ldb, mem_ctx, s2);
191
192         if (b1 && b2) {
193                 /* Both strings converted correctly */
194
195                 u1 = b1;
196                 u2 = b2;
197         } else {
198                 /* One of the strings was not UTF8, so we have no options but to do a binary compare */
199
200                 u1 = s1;
201                 u2 = s2;
202         }
203
204         while (*u1 & *u2) {
205                 if (*u1 != *u2)
206                         break;
207                 if (*u1 == ' ') {
208                         while (u1[0] == u1[1]) u1++;
209                         while (u2[0] == u2[1]) u2++;
210                 }
211                 u1++; u2++;
212         }
213         if (! (*u1 && *u2)) {
214                 while (*u1 == ' ') u1++;
215                 while (*u2 == ' ') u2++;
216         }
217         ret = (int)(*u1 - *u2);
218
219         talloc_free(b1);
220         talloc_free(b2);
221         
222         return ret;
223 }
224
225 /*
226   canonicalise a attribute in DN format
227 */
228 static int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx,
229                                const struct ldb_val *in, struct ldb_val *out)
230 {
231         struct ldb_dn *dn;
232         int ret = -1;
233
234         out->length = 0;
235         out->data = NULL;
236
237         dn = ldb_dn_new(ldb, mem_ctx, (char *)in->data);
238         if ( ! ldb_dn_validate(dn)) {
239                 return -1;
240         }
241
242         out->data = (uint8_t *)ldb_dn_alloc_linearized(mem_ctx, dn);
243         if (out->data == NULL) {
244                 goto done;
245         }
246         out->length = strlen((char *)out->data);
247
248         ret = 0;
249
250 done:
251         talloc_free(dn);
252
253         return ret;
254 }
255
256 /*
257   compare two dns
258 */
259 static int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx,
260                              const struct ldb_val *v1, const struct ldb_val *v2)
261 {
262         struct ldb_dn *dn1 = NULL, *dn2 = NULL;
263         int ret;
264
265         dn1 = ldb_dn_new(ldb, mem_ctx, (char *)v1->data);
266         if ( ! ldb_dn_validate(dn1)) return -1;
267
268         dn2 = ldb_dn_new(ldb, mem_ctx, (char *)v2->data);
269         if ( ! ldb_dn_validate(dn2)) {
270                 talloc_free(dn1);
271                 return -1;
272         } 
273
274         ret = ldb_dn_compare(dn1, dn2);
275
276         talloc_free(dn1);
277         talloc_free(dn2);
278         return ret;
279 }
280
281 /*
282   compare two objectclasses, looking at subclasses
283 */
284 static int ldb_comparison_objectclass(struct ldb_context *ldb, void *mem_ctx,
285                                       const struct ldb_val *v1, const struct ldb_val *v2)
286 {
287         int ret, i;
288         const char **subclasses;
289         ret = ldb_comparison_fold(ldb, mem_ctx, v1, v2);
290         if (ret == 0) {
291                 return 0;
292         }
293         subclasses = ldb_subclass_list(ldb, (char *)v1->data);
294         if (subclasses == NULL) {
295                 return ret;
296         }
297         for (i=0;subclasses[i];i++) {
298                 struct ldb_val vs;
299                 vs.data = discard_const(subclasses[i]);
300                 vs.length = strlen(subclasses[i]);
301                 if (ldb_comparison_objectclass(ldb, mem_ctx, &vs, v2) == 0) {
302                         return 0;
303                 }
304         }
305         return ret;
306 }
307
308 /*
309   compare two utc time values. 1 second resolution
310 */
311 static int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx,
312                                   const struct ldb_val *v1, const struct ldb_val *v2)
313 {
314         time_t t1, t2;
315         t1 = ldb_string_to_time((char *)v1->data);
316         t2 = ldb_string_to_time((char *)v2->data);
317         return (int)t2 - (int)t1;
318 }
319
320 /*
321   canonicalise a utc time
322 */
323 static int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx,
324                                     const struct ldb_val *in, struct ldb_val *out)
325 {
326         time_t t = ldb_string_to_time((char *)in->data);
327         out->data = (uint8_t *)ldb_timestring(mem_ctx, t);
328         if (out->data == NULL) {
329                 return -1;
330         }
331         out->length = strlen((char *)out->data);
332         return 0;
333 }
334
335 /*
336   table of standard attribute handlers
337 */
338 static const struct ldb_attrib_handler ldb_standard_attribs[] = {
339         { 
340                 .attr            = LDB_SYNTAX_INTEGER,
341                 .flags           = 0,
342                 .ldif_read_fn    = ldb_handler_copy,
343                 .ldif_write_fn   = ldb_handler_copy,
344                 .canonicalise_fn = ldb_canonicalise_Integer,
345                 .comparison_fn   = ldb_comparison_Integer
346         },
347         { 
348                 .attr            = LDB_SYNTAX_OCTET_STRING,
349                 .flags           = 0,
350                 .ldif_read_fn    = ldb_handler_copy,
351                 .ldif_write_fn   = ldb_handler_copy,
352                 .canonicalise_fn = ldb_handler_copy,
353                 .comparison_fn   = ldb_comparison_binary
354         },
355         { 
356                 .attr            = LDB_SYNTAX_DIRECTORY_STRING,
357                 .flags           = 0,
358                 .ldif_read_fn    = ldb_handler_copy,
359                 .ldif_write_fn   = ldb_handler_copy,
360                 .canonicalise_fn = ldb_handler_fold,
361                 .comparison_fn   = ldb_comparison_fold
362         },
363         { 
364                 .attr            = LDB_SYNTAX_DN,
365                 .flags           = 0,
366                 .ldif_read_fn    = ldb_handler_copy,
367                 .ldif_write_fn   = ldb_handler_copy,
368                 .canonicalise_fn = ldb_canonicalise_dn,
369                 .comparison_fn   = ldb_comparison_dn
370         },
371         { 
372                 .attr            = LDB_SYNTAX_OBJECTCLASS,
373                 .flags           = 0,
374                 .ldif_read_fn    = ldb_handler_copy,
375                 .ldif_write_fn   = ldb_handler_copy,
376                 .canonicalise_fn = ldb_handler_fold,
377                 .comparison_fn   = ldb_comparison_objectclass
378         },
379         { 
380                 .attr            = LDB_SYNTAX_UTC_TIME,
381                 .flags           = 0,
382                 .ldif_read_fn    = ldb_handler_copy,
383                 .ldif_write_fn   = ldb_handler_copy,
384                 .canonicalise_fn = ldb_canonicalise_utctime,
385                 .comparison_fn   = ldb_comparison_utctime
386         }
387 };
388
389
390 /*
391   return the attribute handlers for a given syntax name
392 */
393 const struct ldb_attrib_handler *ldb_attrib_handler_syntax(struct ldb_context *ldb,
394                                                            const char *syntax)
395 {
396         int i;
397         unsigned num_handlers = sizeof(ldb_standard_attribs)/sizeof(ldb_standard_attribs[0]);
398         /* TODO: should be replaced with a binary search */
399         for (i=0;i<num_handlers;i++) {
400                 if (strcmp(ldb_standard_attribs[i].attr, syntax) == 0) {
401                         return &ldb_standard_attribs[i];
402                 }
403         }
404         return NULL;
405 }
406