r23798: updated old Temple Place FSF addresses to new URL
[gd/samba/.git] / source / 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 "includes.h"
29 #include "ldb/include/includes.h"
30 #include "system/locale.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 (l > 0 && 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         const char *u1, *u2;
157         char *b1, *b2;
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         /* no need to recheck from the start, just from the first utf8 char found */
188         b1 = ldb_casefold(ldb, mem_ctx, s1);
189         b2 = ldb_casefold(ldb, mem_ctx, s2);
190
191         if (b1 && b2) {
192                 /* Both strings converted correctly */
193
194                 u1 = b1;
195                 u2 = b2;
196         } else {
197                 /* One of the strings was not UTF8, so we have no options but to do a binary compare */
198
199                 u1 = s1;
200                 u2 = s2;
201         }
202
203         while (*u1 & *u2) {
204                 if (*u1 != *u2)
205                         break;
206                 if (*u1 == ' ') {
207                         while (u1[0] == u1[1]) u1++;
208                         while (u2[0] == u2[1]) u2++;
209                 }
210                 u1++; u2++;
211         }
212         if (! (*u1 && *u2)) {
213                 while (*u1 == ' ') u1++;
214                 while (*u2 == ' ') u2++;
215         }
216         ret = (int)(*u1 - *u2);
217
218         talloc_free(b1);
219         talloc_free(b2);
220         
221         return ret;
222 }
223
224 /*
225   canonicalise a attribute in DN format
226 */
227 static int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx,
228                                const struct ldb_val *in, struct ldb_val *out)
229 {
230         struct ldb_dn *dn;
231         int ret = -1;
232
233         out->length = 0;
234         out->data = NULL;
235
236         dn = ldb_dn_explode_casefold(ldb, mem_ctx, (char *)in->data);
237         if (dn == NULL) {
238                 return -1;
239         }
240
241         out->data = (uint8_t *)ldb_dn_linearize(mem_ctx, dn);
242         if (out->data == NULL) {
243                 goto done;
244         }
245         out->length = strlen((char *)out->data);
246
247         ret = 0;
248
249 done:
250         talloc_free(dn);
251
252         return ret;
253 }
254
255 /*
256   compare two dns
257 */
258 static int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx,
259                              const struct ldb_val *v1, const struct ldb_val *v2)
260 {
261         struct ldb_dn *dn1 = NULL, *dn2 = NULL;
262         int ret;
263
264         dn1 = ldb_dn_explode_casefold(ldb, mem_ctx, (char *)v1->data);
265         if (dn1 == NULL) return -1;
266
267         dn2 = ldb_dn_explode_casefold(ldb, mem_ctx, (char *)v2->data);
268         if (dn2 == NULL) {
269                 talloc_free(dn1);
270                 return -1;
271         } 
272
273         ret = ldb_dn_compare(ldb, dn1, dn2);
274
275         talloc_free(dn1);
276         talloc_free(dn2);
277         return ret;
278 }
279
280 /*
281   compare two objectclasses, looking at subclasses
282 */
283 static int ldb_comparison_objectclass(struct ldb_context *ldb, void *mem_ctx,
284                                       const struct ldb_val *v1, const struct ldb_val *v2)
285 {
286         int ret, i;
287         const char **subclasses;
288         ret = ldb_comparison_fold(ldb, mem_ctx, v1, v2);
289         if (ret == 0) {
290                 return 0;
291         }
292         subclasses = ldb_subclass_list(ldb, (char *)v1->data);
293         if (subclasses == NULL) {
294                 return ret;
295         }
296         for (i=0;subclasses[i];i++) {
297                 struct ldb_val vs;
298                 vs.data = discard_const_p(uint8_t, subclasses[i]);
299                 vs.length = strlen(subclasses[i]);
300                 if (ldb_comparison_objectclass(ldb, mem_ctx, &vs, v2) == 0) {
301                         return 0;
302                 }
303         }
304         return ret;
305 }
306
307 /*
308   compare two utc time values. 1 second resolution
309 */
310 static int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx,
311                                   const struct ldb_val *v1, const struct ldb_val *v2)
312 {
313         time_t t1, t2;
314         t1 = ldb_string_to_time((char *)v1->data);
315         t2 = ldb_string_to_time((char *)v2->data);
316         return (int)t2 - (int)t1;
317 }
318
319 /*
320   canonicalise a utc time
321 */
322 static int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx,
323                                     const struct ldb_val *in, struct ldb_val *out)
324 {
325         time_t t = ldb_string_to_time((char *)in->data);
326         out->data = (uint8_t *)ldb_timestring(mem_ctx, t);
327         if (out->data == NULL) {
328                 return -1;
329         }
330         out->length = strlen((char *)out->data);
331         return 0;
332 }
333
334 /*
335   table of standard attribute handlers
336 */
337 static const struct ldb_attrib_handler ldb_standard_attribs[] = {
338         { 
339                 .attr            = LDB_SYNTAX_INTEGER,
340                 .flags           = 0,
341                 .ldif_read_fn    = ldb_handler_copy,
342                 .ldif_write_fn   = ldb_handler_copy,
343                 .canonicalise_fn = ldb_canonicalise_Integer,
344                 .comparison_fn   = ldb_comparison_Integer
345         },
346         { 
347                 .attr            = LDB_SYNTAX_OCTET_STRING,
348                 .flags           = 0,
349                 .ldif_read_fn    = ldb_handler_copy,
350                 .ldif_write_fn   = ldb_handler_copy,
351                 .canonicalise_fn = ldb_handler_copy,
352                 .comparison_fn   = ldb_comparison_binary
353         },
354         { 
355                 .attr            = LDB_SYNTAX_DIRECTORY_STRING,
356                 .flags           = 0,
357                 .ldif_read_fn    = ldb_handler_copy,
358                 .ldif_write_fn   = ldb_handler_copy,
359                 .canonicalise_fn = ldb_handler_fold,
360                 .comparison_fn   = ldb_comparison_fold
361         },
362         { 
363                 .attr            = LDB_SYNTAX_DN,
364                 .flags           = 0,
365                 .ldif_read_fn    = ldb_handler_copy,
366                 .ldif_write_fn   = ldb_handler_copy,
367                 .canonicalise_fn = ldb_canonicalise_dn,
368                 .comparison_fn   = ldb_comparison_dn
369         },
370         { 
371                 .attr            = LDB_SYNTAX_OBJECTCLASS,
372                 .flags           = 0,
373                 .ldif_read_fn    = ldb_handler_copy,
374                 .ldif_write_fn   = ldb_handler_copy,
375                 .canonicalise_fn = ldb_handler_fold,
376                 .comparison_fn   = ldb_comparison_objectclass
377         },
378         { 
379                 .attr            = LDB_SYNTAX_UTC_TIME,
380                 .flags           = 0,
381                 .ldif_read_fn    = ldb_handler_copy,
382                 .ldif_write_fn   = ldb_handler_copy,
383                 .canonicalise_fn = ldb_canonicalise_utctime,
384                 .comparison_fn   = ldb_comparison_utctime
385         }
386 };
387
388
389 /*
390   return the attribute handlers for a given syntax name
391 */
392 const struct ldb_attrib_handler *ldb_attrib_handler_syntax(struct ldb_context *ldb,
393                                                            const char *syntax)
394 {
395         int i;
396         unsigned num_handlers = sizeof(ldb_standard_attribs)/sizeof(ldb_standard_attribs[0]);
397         /* TODO: should be replaced with a binary search */
398         for (i=0;i<num_handlers;i++) {
399                 if (strcmp(ldb_standard_attribs[i].attr, syntax) == 0) {
400                         return &ldb_standard_attribs[i];
401                 }
402         }
403         return NULL;
404 }
405