]> git.sur5r.net Git - openldap/blob - servers/slapd/ad.c
Extend value_match to extract an asserted value from a full value
[openldap] / servers / slapd / ad.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /* ad.c - routines for dealing with attribute descriptions */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/ctype.h>
13 #include <ac/errno.h>
14 #include <ac/socket.h>
15 #include <ac/string.h>
16 #include <ac/time.h>
17
18 #include "ldap_pvt.h"
19 #include "slap.h"
20
21 extern ldap_pvt_thread_mutex_t  ad_mutex;       /* init.c */
22
23 static int ad_keystring(
24         struct berval *bv )
25 {
26         ber_len_t i;
27
28         if( !AD_CHAR( bv->bv_val[0] ) ) {
29                 return 1;
30         }
31
32         for( i=1; i<bv->bv_len; i++ ) {
33                 if( !AD_CHAR( bv->bv_val[i] ) ) {
34                         return 1;
35                 }
36         }
37         return 0;
38 }
39
40 int slap_str2ad(
41         const char *str,
42         AttributeDescription **ad,
43         const char **text )
44 {
45         struct berval bv;
46         bv.bv_val = (char *) str;
47         bv.bv_len = strlen( str );
48
49         return slap_bv2ad( &bv, ad, text );
50 }
51
52 int slap_bv2ad(
53         struct berval *bv,
54         AttributeDescription **ad,
55         const char **text )
56 {
57         int rtn = LDAP_UNDEFINED_TYPE;
58         int i;
59         AttributeDescription desc, *d2;
60         char *name, *options;
61
62         assert( ad != NULL );
63         assert( *ad == NULL ); /* temporary */
64
65         if( bv == NULL || bv->bv_len == 0 ) {
66                 *text = "empty attribute description";
67                 return rtn;
68         }
69
70         /* make sure description is IA5 */
71         if( ad_keystring( bv ) ) {
72                 *text = "attribute description contains inappropriate characters";
73                 return rtn;
74         }
75
76         /* find valid base attribute type; parse in place */
77         name = bv->bv_val;
78         options = strchr(name, ';');
79         if (options != NULL) *options = '\0';
80         desc.ad_type = at_find( name );
81         if (options != NULL) *options = ';';
82         if( desc.ad_type == NULL ) {
83                 *text = "attribute type undefined";
84
85                 return rtn;
86         }
87
88         if (options != NULL)
89                 desc.ad_cname.bv_len = options - name;
90         else
91                 desc.ad_cname.bv_len = strlen(name);
92
93         desc.ad_flags = SLAP_DESC_NONE;
94         desc.ad_lang.bv_len = 0;
95         desc.ad_lang.bv_val = NULL;
96
97         /* parse options in place */
98         for( ; options != NULL; ) {
99                 name = options+1;
100                 options = strchr( name, ';' );
101                 if ( options != NULL )
102                         i = options - name;
103                 else
104                         i = strlen(name);
105
106                 if( i == sizeof("binary")-1 && strncasecmp( name, "binary", i) == 0 ) {
107                         if( slap_ad_is_binary( &desc ) ) {
108                                 *text = "option \"binary\" specified multiple times";
109                                 goto done;
110                         }
111
112                         if( !slap_syntax_is_binary( desc.ad_type->sat_syntax )) {
113                                 /* not stored in binary, disallow option */
114                                 *text = "option \"binary\" with type not supported";
115                                 goto done;
116                         }
117
118                         desc.ad_flags |= SLAP_DESC_BINARY;
119
120                 } else if ( i >= sizeof("lang-") && strncasecmp( name, "lang-",
121                         sizeof("lang-")-1 ) == 0)
122                 {
123                         if( desc.ad_lang.bv_len != 0 ) {
124                                 *text = "multiple language tag options specified";
125                                 goto done;
126                         }
127
128                         desc.ad_lang.bv_val = name;
129                         desc.ad_lang.bv_len = i;
130                 } else {
131                         *text = "unrecognized option";
132                         goto done;
133                 }
134         }
135
136         /* see if a matching description is already cached */
137         for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) {
138                 if (d2->ad_flags != desc.ad_flags)
139                         continue;
140                 if (d2->ad_lang.bv_len != desc.ad_lang.bv_len)
141                         continue;
142                 if (d2->ad_lang.bv_len == 0)
143                         break;
144                 if (strncasecmp(d2->ad_lang.bv_val, desc.ad_lang.bv_val,
145                         desc.ad_lang.bv_len) == 0)
146                         break;
147         }
148
149         /* Not found, add new one */
150         while (d2 == NULL) {
151                 int dlen = 0;
152                 /* uses a single mutex instead of one per attributetype.
153                  * I don't believe this is a significant bottleneck. If
154                  * necessary, could change to a per-AttrType rwlock.
155                  */
156                 ldap_pvt_thread_mutex_lock( &ad_mutex );
157                 /* Check again just in case another thread added it */
158                 for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) {
159                         if (d2->ad_flags != desc.ad_flags)
160                                 continue;
161                         if (d2->ad_lang.bv_len != desc.ad_lang.bv_len)
162                                 continue;
163                         if (d2->ad_lang.bv_len == 0)
164                                 break;
165                         if (strncasecmp(d2->ad_lang.bv_val,desc.ad_lang.bv_val,
166                                 desc.ad_lang.bv_len) == 0)
167                                 break;
168                 }
169                 /* Some other thread added it before we got the lock. */
170                 if (d2 != NULL) {
171                         ldap_pvt_thread_mutex_unlock( &ad_mutex );
172                         break;
173                 }
174
175                 /* Allocate a single contiguous block. If there are no
176                  * options, we just need space for the AttrDesc structure.
177                  * Otherwise, we need to tack on the full name length +
178                  * options length.
179                  */
180                 i = sizeof(AttributeDescription);
181                 if (desc.ad_lang.bv_len || desc.ad_flags != SLAP_DESC_NONE) {
182                         if (desc.ad_lang.bv_len)
183                                 dlen = desc.ad_lang.bv_len+1;
184                         dlen += strlen(desc.ad_type->sat_cname)+1;
185                         if( slap_ad_is_binary( &desc ) ) {
186                                 dlen += sizeof("binary");
187                         }
188                 }
189
190                 d2 = ch_malloc(i + dlen);
191                 d2->ad_type = desc.ad_type;
192                 d2->ad_flags = desc.ad_flags;
193                 d2->ad_cname.bv_len = desc.ad_cname.bv_len;
194                 d2->ad_lang.bv_len = desc.ad_lang.bv_len;
195                 if (dlen == 0) {
196                         d2->ad_cname.bv_val = d2->ad_type->sat_cname;
197                         d2->ad_lang.bv_val = NULL;
198                 } else {
199                         d2->ad_cname.bv_val = (char *)(d2+1);
200                         strcpy(d2->ad_cname.bv_val, d2->ad_type->sat_cname);
201                         if( slap_ad_is_binary( &desc ) ) {
202                                 strcpy(d2->ad_cname.bv_val+d2->ad_cname.bv_len,
203                                         ";binary");
204                                 d2->ad_cname.bv_len += sizeof("binary");
205                         }
206                         if( d2->ad_lang.bv_len ) {
207                                 d2->ad_cname.bv_val[d2->ad_cname.bv_len++]=';';
208                                 d2->ad_lang.bv_val = d2->ad_cname.bv_val+
209                                         d2->ad_cname.bv_len;
210                                 strncpy(d2->ad_lang.bv_val,desc.ad_lang.bv_val,
211                                         d2->ad_lang.bv_len);
212                                 d2->ad_lang.bv_val[d2->ad_lang.bv_len] = '\0';
213                                 ldap_pvt_str2lower(d2->ad_lang.bv_val);
214                                 d2->ad_cname.bv_len += d2->ad_lang.bv_len;
215                         }
216                 }
217                 /* Add new desc to list. We always want the bare Desc with
218                  * no options to stay at the head of the list, assuming
219                  * that one will be used most frequently.
220                  */
221                 if (desc.ad_type->sat_ad == NULL || dlen == 0) {
222                         d2->ad_next = desc.ad_type->sat_ad;
223                         desc.ad_type->sat_ad = d2;
224                 } else {
225                         d2->ad_next = desc.ad_type->sat_ad->ad_next;
226                         desc.ad_type->sat_ad->ad_next = d2;
227                 }
228                 ldap_pvt_thread_mutex_unlock( &ad_mutex );
229         }
230
231         if( *ad == NULL ) {
232                 *ad = d2;
233         } else {
234                 **ad = *d2;
235         }
236
237         rtn = LDAP_SUCCESS;
238
239 done:
240         return rtn;
241 }
242
243 int is_ad_subtype(
244         AttributeDescription *sub,
245         AttributeDescription *super
246 )
247 {
248         if( !is_at_subtype( sub->ad_type, super->ad_type ) ) {
249                 return 0;
250         }
251
252         if( super->ad_flags && ( super->ad_flags != sub->ad_flags )) {
253                 return 0;
254         }
255
256         if( super->ad_lang.bv_len && (sub->ad_lang.bv_len !=
257                 super->ad_lang.bv_len || strcmp( super->ad_lang.bv_val,
258                 sub->ad_lang.bv_val)))
259         {
260                 return 0;
261         }
262
263         return 1;
264 }
265
266
267 int ad_inlist(
268         AttributeDescription *desc,
269         char **attrs )
270 {
271         int i;
272         for( i=0; attrs[i] != NULL; i++ ) {
273                 AttributeDescription *ad = NULL;
274                 const char *text;
275                 int rc;
276                 
277                 rc = slap_str2ad( attrs[i], &ad, &text );
278
279                 if( rc != LDAP_SUCCESS ) continue;
280
281                 rc = is_ad_subtype( desc, ad );
282
283                 if( rc ) return 1;
284         }
285
286         return 0;
287 }
288
289
290 int slap_str2undef_ad(
291         const char *str,
292         AttributeDescription **ad,
293         const char **text )
294 {
295         struct berval bv;
296         bv.bv_val = (char *) str;
297         bv.bv_len = strlen( str );
298
299         return slap_bv2undef_ad( &bv, ad, text );
300 }
301
302 int slap_bv2undef_ad(
303         struct berval *bv,
304         AttributeDescription **ad,
305         const char **text )
306 {
307         AttributeDescription *desc;
308
309         assert( ad != NULL );
310
311         if( bv == NULL || bv->bv_len == 0 ) {
312                 *text = "empty attribute description";
313                 return LDAP_UNDEFINED_TYPE;
314         }
315
316         /* make sure description is IA5 */
317         if( ad_keystring( bv ) ) {
318                 *text = "attribute description contains inappropriate characters";
319                 return LDAP_UNDEFINED_TYPE;
320         }
321
322         for (desc = slap_schema.si_at_undefined->sat_ad; desc;
323                 desc=desc->ad_next)
324                 if (desc->ad_cname.bv_len == bv->bv_len &&
325                     !strcasecmp(desc->ad_cname.bv_val, bv->bv_val))
326                         break;
327         
328         if (!desc) {
329                 desc = ch_malloc(sizeof(AttributeDescription) +
330                         bv->bv_len + 1);
331                 
332                 desc->ad_flags = SLAP_DESC_NONE;
333                 desc->ad_lang.bv_val = NULL;
334                 desc->ad_lang.bv_len = 0;
335
336                 desc->ad_cname.bv_len = bv->bv_len;
337                 desc->ad_cname.bv_val = (char *)(desc+1);
338                 strcpy(desc->ad_cname.bv_val, bv->bv_val);
339
340                 /* canonical to upper case */
341                 ldap_pvt_str2upper( desc->ad_cname.bv_val );
342
343                 desc->ad_type = slap_schema.si_at_undefined;
344                 desc->ad_next = desc->ad_type->sat_ad;
345                 desc->ad_type->sat_ad = desc;
346         }
347
348         if (!*ad)
349                 *ad = desc;
350         else
351                 **ad = *desc;
352
353         return LDAP_SUCCESS;
354 }