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