]> git.sur5r.net Git - openldap/blob - servers/slapd/ad.c
ITS#5935
[openldap] / servers / slapd / ad.c
1 /* ad.c - routines for dealing with attribute descriptions */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2009 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20
21 #include <ac/ctype.h>
22 #include <ac/errno.h>
23 #include <ac/socket.h>
24 #include <ac/string.h>
25 #include <ac/time.h>
26
27 #include "slap.h"
28 #include "lutil.h"
29
30 static AttributeName anlist_no_attrs[] = {
31         { BER_BVC( LDAP_NO_ATTRS ), NULL, 0, NULL },
32         { BER_BVNULL, NULL, 0, NULL }
33 };
34
35 static AttributeName anlist_all_user_attributes[] = {
36         { BER_BVC( LDAP_ALL_USER_ATTRIBUTES ), NULL, 0, NULL },
37         { BER_BVNULL, NULL, 0, NULL }
38 };
39
40 static AttributeName anlist_all_operational_attributes[] = {
41         { BER_BVC( LDAP_ALL_OPERATIONAL_ATTRIBUTES ), NULL, 0, NULL },
42         { BER_BVNULL, NULL, 0, NULL }
43 };
44
45 static AttributeName anlist_all_attributes[] = {
46         { BER_BVC( LDAP_ALL_USER_ATTRIBUTES ), NULL, 0, NULL },
47         { BER_BVC( LDAP_ALL_OPERATIONAL_ATTRIBUTES ), NULL, 0, NULL },
48         { BER_BVNULL, NULL, 0, NULL }
49 };
50
51 AttributeName *slap_anlist_no_attrs = anlist_no_attrs;
52 AttributeName *slap_anlist_all_user_attributes = anlist_all_user_attributes;
53 AttributeName *slap_anlist_all_operational_attributes = anlist_all_operational_attributes;
54 AttributeName *slap_anlist_all_attributes = anlist_all_attributes;
55
56 typedef struct Attr_option {
57         struct berval name;     /* option name or prefix */
58         int           prefix;   /* NAME is a tag and range prefix */
59 } Attr_option;
60
61 static Attr_option lang_option = { BER_BVC("lang-"), 1 };
62
63 /* Options sorted by name, and number of options */
64 static Attr_option *options = &lang_option;
65 static int option_count = 1;
66
67 static int msad_range_hack = 0;
68
69 static Attr_option *ad_find_option_definition( const char *opt, int optlen );
70
71 static int ad_keystring(
72         struct berval *bv )
73 {
74         ber_len_t i;
75
76         if( !AD_LEADCHAR( bv->bv_val[0] ) ) {
77                 return 1;
78         }
79
80         for( i=1; i<bv->bv_len; i++ ) {
81                 if( !AD_CHAR( bv->bv_val[i] )) {
82                         if ( msad_range_hack && bv->bv_val[i] == '=' )
83                                 continue;
84                         return 1;
85                 }
86         }
87         return 0;
88 }
89
90 void ad_destroy( AttributeDescription *ad )
91 {
92         AttributeDescription *n;
93
94         for (; ad != NULL; ad = n) {
95                 n = ad->ad_next;
96                 ldap_memfree( ad );
97         }
98 }
99
100 /* Is there an AttributeDescription for this type that uses these tags? */
101 AttributeDescription * ad_find_tags(
102         AttributeType *type,
103         struct berval *tags )
104 {
105         AttributeDescription *ad;
106
107         ldap_pvt_thread_mutex_lock( &type->sat_ad_mutex );
108         for (ad = type->sat_ad; ad; ad=ad->ad_next)
109         {
110                 if (ad->ad_tags.bv_len == tags->bv_len &&
111                         !strcasecmp(ad->ad_tags.bv_val, tags->bv_val))
112                         break;
113         }
114         ldap_pvt_thread_mutex_unlock( &type->sat_ad_mutex );
115         return ad;
116 }
117
118 int slap_str2ad(
119         const char *str,
120         AttributeDescription **ad,
121         const char **text )
122 {
123         struct berval bv;
124         bv.bv_val = (char *) str;
125         bv.bv_len = strlen( str );
126
127         return slap_bv2ad( &bv, ad, text );
128 }
129
130 static char *strchrlen(
131         const char *beg, 
132         const char *end,
133         const char ch, 
134         int *len )
135 {
136         const char *p;
137
138         for( p=beg; *p && p < end; p++ ) {
139                 if( *p == ch ) {
140                         *len = p - beg;
141                         return (char *) p;
142                 }
143         }
144
145         *len = p - beg;
146         return NULL;
147 }
148
149 int slap_bv2ad(
150         struct berval *bv,
151         AttributeDescription **ad,
152         const char **text )
153 {
154         int rtn = LDAP_UNDEFINED_TYPE;
155         AttributeDescription desc, *d2;
156         char *name, *options, *optn;
157         char *opt, *next;
158         int ntags;
159         int tagslen;
160
161         /* hardcoded limits for speed */
162 #define MAX_TAGGING_OPTIONS 128
163         struct berval tags[MAX_TAGGING_OPTIONS+1];
164 #define MAX_TAGS_LEN 1024
165         char tagbuf[MAX_TAGS_LEN];
166
167         assert( ad != NULL );
168         assert( *ad == NULL ); /* temporary */
169
170         if( bv == NULL || BER_BVISNULL( bv ) || BER_BVISEMPTY( bv ) ) {
171                 *text = "empty AttributeDescription";
172                 return rtn;
173         }
174
175         /* make sure description is IA5 */
176         if( ad_keystring( bv ) ) {
177                 *text = "AttributeDescription contains inappropriate characters";
178                 return rtn;
179         }
180
181         /* find valid base attribute type; parse in place */
182         desc.ad_cname = *bv;
183         desc.ad_flags = 0;
184         BER_BVZERO( &desc.ad_tags );
185         name = bv->bv_val;
186         options = ber_bvchr( bv, ';' );
187         if ( options != NULL && (unsigned) ( options - name ) < bv->bv_len ) {
188                 /* don't go past the end of the berval! */
189                 desc.ad_cname.bv_len = options - name;
190         } else {
191                 options = NULL;
192         }
193         desc.ad_type = at_bvfind( &desc.ad_cname );
194         if( desc.ad_type == NULL ) {
195                 *text = "attribute type undefined";
196                 return rtn;
197         }
198
199         if( is_at_operational( desc.ad_type ) && options != NULL ) {
200                 *text = "operational attribute with options undefined";
201                 return rtn;
202         }
203
204         /*
205          * parse options in place
206          */
207         ntags = 0;
208         tagslen = 0;
209         optn = bv->bv_val + bv->bv_len;
210
211         for( opt=options; opt != NULL; opt=next ) {
212                 int optlen;
213                 opt++; 
214                 next = strchrlen( opt, optn, ';', &optlen );
215
216                 if( optlen == 0 ) {
217                         *text = "zero length option is invalid";
218                         return rtn;
219                 
220                 } else if ( optlen == STRLENOF("binary") &&
221                         strncasecmp( opt, "binary", STRLENOF("binary") ) == 0 )
222                 {
223                         /* binary option */
224                         if( slap_ad_is_binary( &desc ) ) {
225                                 *text = "option \"binary\" specified multiple times";
226                                 return rtn;
227                         }
228
229                         if( !slap_syntax_is_binary( desc.ad_type->sat_syntax )) {
230                                 /* not stored in binary, disallow option */
231                                 *text = "option \"binary\" not supported with type";
232                                 return rtn;
233                         }
234
235                         desc.ad_flags |= SLAP_DESC_BINARY;
236                         continue;
237
238                 } else if ( ad_find_option_definition( opt, optlen ) ) {
239                         int i;
240
241                         if( opt[optlen-1] == '-' ||
242                                 ( opt[optlen-1] == '=' && msad_range_hack )) {
243                                 desc.ad_flags |= SLAP_DESC_TAG_RANGE;
244                         }
245
246                         if( ntags >= MAX_TAGGING_OPTIONS ) {
247                                 *text = "too many tagging options";
248                                 return rtn;
249                         }
250
251                         /*
252                          * tags should be presented in sorted order,
253                          * so run the array in reverse.
254                          */
255                         for( i=ntags-1; i>=0; i-- ) {
256                                 int rc;
257
258                                 rc = strncasecmp( opt, tags[i].bv_val,
259                                         (unsigned) optlen < tags[i].bv_len
260                                                 ? (unsigned) optlen : tags[i].bv_len );
261
262                                 if( rc == 0 && (unsigned)optlen == tags[i].bv_len ) {
263                                         /* duplicate (ignore) */
264                                         goto done;
265
266                                 } else if ( rc > 0 ||
267                                         ( rc == 0 && (unsigned)optlen > tags[i].bv_len ))
268                                 {
269                                         AC_MEMCPY( &tags[i+2], &tags[i+1],
270                                                 (ntags-i-1)*sizeof(struct berval) );
271                                         tags[i+1].bv_val = opt;
272                                         tags[i+1].bv_len = optlen;
273                                         goto done;
274                                 }
275                         }
276
277                         if( ntags ) {
278                                 AC_MEMCPY( &tags[1], &tags[0],
279                                         ntags*sizeof(struct berval) );
280                         }
281                         tags[0].bv_val = opt;
282                         tags[0].bv_len = optlen;
283
284 done:;
285                         tagslen += optlen + 1;
286                         ntags++;
287
288                 } else {
289                         *text = "unrecognized option";
290                         return rtn;
291                 }
292         }
293
294         if( ntags > 0 ) {
295                 int i;
296
297                 if( tagslen > MAX_TAGS_LEN ) {
298                         *text = "tagging options too long";
299                         return rtn;
300                 }
301
302                 desc.ad_tags.bv_val = tagbuf;
303                 tagslen = 0;
304
305                 for( i=0; i<ntags; i++ ) {
306                         AC_MEMCPY( &desc.ad_tags.bv_val[tagslen],
307                                 tags[i].bv_val, tags[i].bv_len );
308
309                         tagslen += tags[i].bv_len;
310                         desc.ad_tags.bv_val[tagslen++] = ';';
311                 }
312
313                 desc.ad_tags.bv_val[--tagslen] = '\0';
314                 desc.ad_tags.bv_len = tagslen;
315         }
316
317         /* see if a matching description is already cached */
318         for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) {
319                 if( d2->ad_flags != desc.ad_flags ) {
320                         continue;
321                 }
322                 if( d2->ad_tags.bv_len != desc.ad_tags.bv_len ) {
323                         continue;
324                 }
325                 if( d2->ad_tags.bv_len == 0 ) {
326                         break;
327                 }
328                 if( strncasecmp( d2->ad_tags.bv_val, desc.ad_tags.bv_val,
329                         desc.ad_tags.bv_len ) == 0 )
330                 {
331                         break;
332                 }
333         }
334
335         /* Not found, add new one */
336         while (d2 == NULL) {
337                 size_t dlen = 0;
338                 ldap_pvt_thread_mutex_lock( &desc.ad_type->sat_ad_mutex );
339                 /* check again now that we've locked */
340                 for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) {
341                         if (d2->ad_flags != desc.ad_flags)
342                                 continue;
343                         if (d2->ad_tags.bv_len != desc.ad_tags.bv_len)
344                                 continue;
345                         if (d2->ad_tags.bv_len == 0)
346                                 break;
347                         if (strncasecmp(d2->ad_tags.bv_val, desc.ad_tags.bv_val,
348                                 desc.ad_tags.bv_len) == 0)
349                                 break;
350                 }
351                 if (d2) {
352                         ldap_pvt_thread_mutex_unlock( &desc.ad_type->sat_ad_mutex );
353                         break;
354                 }
355
356                 /* Allocate a single contiguous block. If there are no
357                  * options, we just need space for the AttrDesc structure.
358                  * Otherwise, we need to tack on the full name length +
359                  * options length, + maybe tagging options length again.
360                  */
361                 if (desc.ad_tags.bv_len || desc.ad_flags != SLAP_DESC_NONE) {
362                         dlen = desc.ad_type->sat_cname.bv_len + 1;
363                         if (desc.ad_tags.bv_len) {
364                                 dlen += 1 + desc.ad_tags.bv_len;
365                         }
366                         if ( slap_ad_is_binary( &desc ) ) {
367                                 dlen += 1 + STRLENOF(";binary") + desc.ad_tags.bv_len;
368                         }
369                 }
370
371                 d2 = ch_malloc(sizeof(AttributeDescription) + dlen);
372                 d2->ad_next = NULL;
373                 d2->ad_type = desc.ad_type;
374                 d2->ad_flags = desc.ad_flags;
375                 d2->ad_cname.bv_len = desc.ad_type->sat_cname.bv_len;
376                 d2->ad_tags.bv_len = desc.ad_tags.bv_len;
377
378                 if (dlen == 0) {
379                         d2->ad_cname.bv_val = d2->ad_type->sat_cname.bv_val;
380                         d2->ad_tags.bv_val = NULL;
381                 } else {
382                         char *cp, *op, *lp;
383                         int j;
384                         d2->ad_cname.bv_val = (char *)(d2+1);
385                         strcpy(d2->ad_cname.bv_val, d2->ad_type->sat_cname.bv_val);
386                         cp = d2->ad_cname.bv_val + d2->ad_cname.bv_len;
387                         if( slap_ad_is_binary( &desc ) ) {
388                                 op = cp;
389                                 lp = NULL;
390                                 if( desc.ad_tags.bv_len ) {
391                                         lp = desc.ad_tags.bv_val;
392                                         while( strncasecmp(lp, "binary", STRLENOF("binary")) < 0
393                                                && (lp = strchr( lp, ';' )) != NULL )
394                                                 ++lp;
395                                         if( lp != desc.ad_tags.bv_val ) {
396                                                 *cp++ = ';';
397                                                 j = (lp
398                                                      ? (unsigned) (lp - desc.ad_tags.bv_val - 1)
399                                                      : strlen( desc.ad_tags.bv_val ));
400                                                 cp = lutil_strncopy(cp, desc.ad_tags.bv_val, j);
401                                         }
402                                 }
403                                 cp = lutil_strcopy(cp, ";binary");
404                                 if( lp != NULL ) {
405                                         *cp++ = ';';
406                                         cp = lutil_strcopy(cp, lp);
407                                 }
408                                 d2->ad_cname.bv_len = cp - d2->ad_cname.bv_val;
409                                 if( desc.ad_tags.bv_len )
410                                         ldap_pvt_str2lower(op);
411                                 j = 1;
412                         } else {
413                                 j = 0;
414                         }
415                         if( desc.ad_tags.bv_len ) {
416                                 lp = d2->ad_cname.bv_val + d2->ad_cname.bv_len + j;
417                                 if ( j == 0 )
418                                         *lp++ = ';';
419                                 d2->ad_tags.bv_val = lp;
420                                 strcpy(lp, desc.ad_tags.bv_val);
421                                 ldap_pvt_str2lower(lp);
422                                 if( j == 0 )
423                                         d2->ad_cname.bv_len += 1 + desc.ad_tags.bv_len;
424                         }
425                 }
426                 /* Add new desc to list. We always want the bare Desc with
427                  * no options to stay at the head of the list, assuming
428                  * that one will be used most frequently.
429                  */
430                 if (desc.ad_type->sat_ad == NULL || dlen == 0) {
431                         d2->ad_next = desc.ad_type->sat_ad;
432                         desc.ad_type->sat_ad = d2;
433                 } else {
434                         d2->ad_next = desc.ad_type->sat_ad->ad_next;
435                         desc.ad_type->sat_ad->ad_next = d2;
436                 }
437                 ldap_pvt_thread_mutex_unlock( &desc.ad_type->sat_ad_mutex );
438         }
439
440         if( *ad == NULL ) {
441                 *ad = d2;
442         } else {
443                 **ad = *d2;
444         }
445
446         return LDAP_SUCCESS;
447 }
448
449 static int is_ad_subtags(
450         struct berval *subtagsbv, 
451         struct berval *suptagsbv )
452 {
453         const char *suptags, *supp, *supdelimp, *supn;
454         const char *subtags, *subp, *subdelimp, *subn;
455         int  suplen, sublen;
456
457         subtags =subtagsbv->bv_val;
458         suptags =suptagsbv->bv_val;
459         subn = subtags + subtagsbv->bv_len;
460         supn = suptags + suptagsbv->bv_len;
461
462         for( supp=suptags ; supp; supp=supdelimp ) {
463                 supdelimp = strchrlen( supp, supn, ';', &suplen );
464                 if( supdelimp ) supdelimp++;
465
466                 for( subp=subtags ; subp; subp=subdelimp ) {
467                         subdelimp = strchrlen( subp, subn, ';', &sublen );
468                         if( subdelimp ) subdelimp++;
469
470                         if ( suplen > sublen
471                                  ? ( suplen-1 == sublen && supp[suplen-1] == '-'
472                                          && strncmp( supp, subp, sublen ) == 0 )
473                                  : ( ( suplen == sublen || supp[suplen-1] == '-' )
474                                          && strncmp( supp, subp, suplen ) == 0 ) )
475                         {
476                                 goto match;
477                         }
478                 }
479
480                 return 0;
481 match:;
482         }
483         return 1;
484 }
485
486 int is_ad_subtype(
487         AttributeDescription *sub,
488         AttributeDescription *super
489 )
490 {
491         AttributeType *a;
492         int lr;
493
494         for ( a = sub->ad_type; a; a=a->sat_sup ) {
495                 if ( a == super->ad_type ) break;
496         }
497         if( !a ) {
498                 return 0;
499         }
500
501         /* ensure sub does support all flags of super */
502         lr = sub->ad_tags.bv_len ? SLAP_DESC_TAG_RANGE : 0;
503         if(( super->ad_flags & ( sub->ad_flags | lr )) != super->ad_flags ) {
504                 return 0;
505         }
506
507         /* check for tagging options */
508         if ( super->ad_tags.bv_len == 0 )
509                 return 1;
510         if ( sub->ad_tags.bv_len == 0 )
511                 return 0;
512
513         return is_ad_subtags( &sub->ad_tags, &super->ad_tags );
514 }
515
516 int ad_inlist(
517         AttributeDescription *desc,
518         AttributeName *attrs )
519 {
520         if (! attrs ) return 0;
521
522         for( ; attrs->an_name.bv_val; attrs++ ) {
523                 AttributeType *a;
524                 ObjectClass *oc;
525                 
526                 if ( attrs->an_desc ) {
527                         int lr;
528
529                         if ( desc == attrs->an_desc ) {
530                                 return 1;
531                         }
532
533                         /*
534                          * EXTENSION: if requested description is preceeded by
535                          * a '-' character, do not match on subtypes.
536                          */
537                         if ( attrs->an_name.bv_val[0] == '-' ) {
538                                 continue;
539                         }
540                         
541                         /* Is this a subtype of the requested attr? */
542                         for (a = desc->ad_type; a; a=a->sat_sup) {
543                                 if ( a == attrs->an_desc->ad_type )
544                                         break;
545                         }
546                         if ( !a ) {
547                                 continue;
548                         }
549                         /* Does desc support all the requested flags? */
550                         lr = desc->ad_tags.bv_len ? SLAP_DESC_TAG_RANGE : 0;
551                         if(( attrs->an_desc->ad_flags & (desc->ad_flags | lr))
552                                 != attrs->an_desc->ad_flags ) {
553                                 continue;
554                         }
555                         /* Do the descs have compatible tags? */
556                         if ( attrs->an_desc->ad_tags.bv_len == 0 ) {
557                                 return 1;
558                         }
559                         if ( desc->ad_tags.bv_len == 0) {
560                                 continue;
561                         }
562                         if ( is_ad_subtags( &desc->ad_tags,
563                                 &attrs->an_desc->ad_tags ) ) {
564                                 return 1;
565                         }
566                         continue;
567                 }
568
569                 if ( ber_bvccmp( &attrs->an_name, '*' ) ) {
570                         if ( !is_at_operational( desc->ad_type ) ) {
571                                 return 1;
572                         }
573                         continue;
574                 }
575
576                 if ( ber_bvccmp( &attrs->an_name, '+' ) ) {
577                         if ( is_at_operational( desc->ad_type ) ) {
578                                 return 1;
579                         }
580                         continue;
581                 }
582
583                 /*
584                  * EXTENSION: see if requested description is @objectClass
585                  * if so, return attributes which the class requires/allows
586                  * else if requested description is !objectClass, return
587                  * attributes which the class does not require/allow
588                  */
589                 oc = attrs->an_oc;
590                 if( oc == NULL && attrs->an_name.bv_val ) {
591                         switch( attrs->an_name.bv_val[0] ) {
592                         case '@': /* @objectClass */
593                         case '+': /* +objectClass (deprecated) */
594                         case '!': { /* exclude */
595                                         struct berval ocname;
596                                         ocname.bv_len = attrs->an_name.bv_len - 1;
597                                         ocname.bv_val = &attrs->an_name.bv_val[1];
598                                         oc = oc_bvfind( &ocname );
599                                         attrs->an_oc_exclude = 0;
600                                         if ( oc && attrs->an_name.bv_val[0] == '!' ) {
601                                                 attrs->an_oc_exclude = 1;
602                                         }
603                                 } break;
604
605                         default: /* old (deprecated) way */
606                                 oc = oc_bvfind( &attrs->an_name );
607                         }
608                         attrs->an_oc = oc;
609                 }
610                 if( oc != NULL ) {
611                         if ( attrs->an_oc_exclude ) {
612                                 if ( oc == slap_schema.si_oc_extensibleObject ) {
613                                         /* extensibleObject allows the return of anything */
614                                         return 0;
615                                 }
616
617                                 if( oc->soc_required ) {
618                                         /* allow return of required attributes */
619                                         int i;
620                                 
621                                         for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
622                                                 for (a = desc->ad_type; a; a=a->sat_sup) {
623                                                         if ( a == oc->soc_required[i] ) {
624                                                                 return 0;
625                                                         }
626                                                 }
627                                         }
628                                 }
629
630                                 if( oc->soc_allowed ) {
631                                         /* allow return of allowed attributes */
632                                         int i;
633                                         for ( i = 0; oc->soc_allowed[i] != NULL; i++ ) {
634                                                 for (a = desc->ad_type; a; a=a->sat_sup) {
635                                                         if ( a == oc->soc_allowed[i] ) {
636                                                                 return 0;
637                                                         }
638                                                 }
639                                         }
640                                 }
641
642                                 return 1;
643                         }
644                         
645                         if ( oc == slap_schema.si_oc_extensibleObject ) {
646                                 /* extensibleObject allows the return of anything */
647                                 return 1;
648                         }
649
650                         if( oc->soc_required ) {
651                                 /* allow return of required attributes */
652                                 int i;
653                                 
654                                 for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
655                                         for (a = desc->ad_type; a; a=a->sat_sup) {
656                                                 if ( a == oc->soc_required[i] ) {
657                                                         return 1;
658                                                 }
659                                         }
660                                 }
661                         }
662
663                         if( oc->soc_allowed ) {
664                                 /* allow return of allowed attributes */
665                                 int i;
666                                 for ( i = 0; oc->soc_allowed[i] != NULL; i++ ) {
667                                         for (a = desc->ad_type; a; a=a->sat_sup) {
668                                                 if ( a == oc->soc_allowed[i] ) {
669                                                         return 1;
670                                                 }
671                                         }
672                                 }
673                         }
674
675                 } else {
676                         const char      *text;
677
678                         /* give it a chance of being retrieved by a proxy... */
679                         (void)slap_bv2undef_ad( &attrs->an_name,
680                                 &attrs->an_desc, &text,
681                                 SLAP_AD_PROXIED|SLAP_AD_NOINSERT );
682                 }
683         }
684
685         return 0;
686 }
687
688
689 int slap_str2undef_ad(
690         const char *str,
691         AttributeDescription **ad,
692         const char **text,
693         unsigned flags )
694 {
695         struct berval bv;
696         bv.bv_val = (char *) str;
697         bv.bv_len = strlen( str );
698
699         return slap_bv2undef_ad( &bv, ad, text, flags );
700 }
701
702 int slap_bv2undef_ad(
703         struct berval *bv,
704         AttributeDescription **ad,
705         const char **text,
706         unsigned flags )
707 {
708         AttributeDescription *desc;
709         AttributeType *at;
710
711         assert( ad != NULL );
712
713         if( bv == NULL || bv->bv_len == 0 ) {
714                 *text = "empty AttributeDescription";
715                 return LDAP_UNDEFINED_TYPE;
716         }
717
718         /* make sure description is IA5 */
719         if( ad_keystring( bv ) ) {
720                 *text = "AttributeDescription contains inappropriate characters";
721                 return LDAP_UNDEFINED_TYPE;
722         }
723
724         /* use the appropriate type */
725         if ( flags & SLAP_AD_PROXIED ) {
726                 at = slap_schema.si_at_proxied;
727
728         } else {
729                 at = slap_schema.si_at_undefined;
730         }
731
732         for( desc = at->sat_ad; desc; desc=desc->ad_next ) {
733                 if( desc->ad_cname.bv_len == bv->bv_len &&
734                     !strcasecmp( desc->ad_cname.bv_val, bv->bv_val ) )
735                 {
736                         break;
737                 }
738         }
739
740         if( !desc ) {
741                 if ( flags & SLAP_AD_NOINSERT ) {
742                         *text = NULL;
743                         return LDAP_UNDEFINED_TYPE;
744                 }
745         
746                 desc = ch_malloc(sizeof(AttributeDescription) + 1 +
747                         bv->bv_len);
748                 
749                 desc->ad_flags = SLAP_DESC_NONE;
750                 BER_BVZERO( &desc->ad_tags );
751
752                 desc->ad_cname.bv_len = bv->bv_len;
753                 desc->ad_cname.bv_val = (char *)(desc+1);
754                 strcpy(desc->ad_cname.bv_val, bv->bv_val);
755
756                 /* canonical to upper case */
757                 ldap_pvt_str2upper( desc->ad_cname.bv_val );
758
759                 /* shouldn't we protect this for concurrency? */
760                 desc->ad_type = at;
761                 ldap_pvt_thread_mutex_lock( &ad_undef_mutex );
762                 desc->ad_next = desc->ad_type->sat_ad;
763                 desc->ad_type->sat_ad = desc;
764                 ldap_pvt_thread_mutex_unlock( &ad_undef_mutex );
765
766                 Debug( LDAP_DEBUG_ANY,
767                         "%s attributeDescription \"%s\" inserted.\n",
768                         ( flags & SLAP_AD_PROXIED ) ? "PROXIED" : "UNKNOWN",
769                         desc->ad_cname.bv_val, 0 );
770         }
771
772         if( !*ad ) {
773                 *ad = desc;
774         } else {
775                 **ad = *desc;
776         }
777
778         return LDAP_SUCCESS;
779 }
780
781 AttributeDescription *
782 slap_bv2tmp_ad(
783         struct berval *bv,
784         void *memctx )
785 {
786         AttributeDescription *ad =
787                  slap_sl_mfuncs.bmf_malloc( sizeof(AttributeDescription) +
788                         bv->bv_len + 1, memctx );
789
790         ad->ad_cname.bv_val = (char *)(ad+1);
791         strncpy( ad->ad_cname.bv_val, bv->bv_val, bv->bv_len+1 );
792         ad->ad_cname.bv_len = bv->bv_len;
793         ad->ad_flags = SLAP_DESC_TEMPORARY;
794         ad->ad_type = slap_schema.si_at_undefined;
795
796         return ad;
797 }
798
799 static int
800 undef_promote(
801         AttributeType   *at,
802         char            *name,
803         AttributeType   *nat )
804 {
805         AttributeDescription    **u_ad, **n_ad;
806
807         /* Get to last ad on the new type */
808         for ( n_ad = &nat->sat_ad; *n_ad; n_ad = &(*n_ad)->ad_next ) ;
809
810         for ( u_ad = &at->sat_ad; *u_ad; ) {
811                 struct berval   bv;
812
813                 ber_str2bv( name, 0, 0, &bv );
814
815                 /* remove iff undef == name or undef == name;tag */
816                 if ( (*u_ad)->ad_cname.bv_len >= bv.bv_len
817                         && strncasecmp( (*u_ad)->ad_cname.bv_val, bv.bv_val, bv.bv_len ) == 0
818                         && ( (*u_ad)->ad_cname.bv_val[ bv.bv_len ] == '\0'
819                                 || (*u_ad)->ad_cname.bv_val[ bv.bv_len ] == ';' ) )
820                 {
821                         AttributeDescription    *tmp = *u_ad;
822
823                         *u_ad = (*u_ad)->ad_next;
824
825                         tmp->ad_type = nat;
826                         tmp->ad_next = NULL;
827                         /* ad_cname was contiguous, no leak here */
828                         tmp->ad_cname = nat->sat_cname;
829                         *n_ad = tmp;
830                         n_ad = &tmp->ad_next;
831                 } else {
832                         u_ad = &(*u_ad)->ad_next;
833                 }
834         }
835
836         return 0;
837 }
838
839 int
840 slap_ad_undef_promote(
841         char *name,
842         AttributeType *at )
843 {
844         int     rc;
845
846         ldap_pvt_thread_mutex_lock( &ad_undef_mutex );
847
848         rc = undef_promote( slap_schema.si_at_undefined, name, at );
849         if ( rc == 0 ) {
850                 rc = undef_promote( slap_schema.si_at_proxied, name, at );
851         }
852
853         ldap_pvt_thread_mutex_unlock( &ad_undef_mutex );
854
855         return rc;
856 }
857
858 int
859 an_find(
860     AttributeName *a,
861     struct berval *s
862 )
863 {
864         if( a == NULL ) return 0;
865
866         for ( ; a->an_name.bv_val; a++ ) {
867                 if ( a->an_name.bv_len != s->bv_len) continue;
868                 if ( strcasecmp( s->bv_val, a->an_name.bv_val ) == 0 ) {
869                         return( 1 );
870                 }
871         }
872
873         return( 0 );
874 }
875
876 /*
877  * Convert a delimited string into a list of AttributeNames; add
878  * on to an existing list if it was given.  If the string is not
879  * a valid attribute name, if a '-' is prepended it is skipped
880  * and the remaining name is tried again; if a '@' (or '+') is
881  * prepended, an objectclass name is searched instead; if a '!'
882  * is prepended, the objectclass name is negated.
883  * 
884  * NOTE: currently, if a valid attribute name is not found, the
885  * same string is also checked as valid objectclass name; however,
886  * this behavior is deprecated.
887  */
888 AttributeName *
889 str2anlist( AttributeName *an, char *in, const char *brkstr )
890 {
891         char    *str;
892         char    *s;
893         char    *lasts;
894         int     i, j;
895         const char *text;
896         AttributeName *anew;
897
898         /* find last element in list */
899         i = 0;
900         if ( an != NULL ) {
901                 for ( i = 0; !BER_BVISNULL( &an[ i ].an_name ) ; i++)
902                         ;
903         }
904         
905         /* protect the input string from strtok */
906         str = ch_strdup( in );
907
908         /* Count words in string */
909         j = 1;
910         for ( s = str; *s; s++ ) {
911                 if ( strchr( brkstr, *s ) != NULL ) {
912                         j++;
913                 }
914         }
915
916         an = ch_realloc( an, ( i + j + 1 ) * sizeof( AttributeName ) );
917         anew = an + i;
918         for ( s = ldap_pvt_strtok( str, brkstr, &lasts );
919                 s != NULL;
920                 s = ldap_pvt_strtok( NULL, brkstr, &lasts ) )
921         {
922                 /* put a stop mark */
923                 BER_BVZERO( &anew[1].an_name );
924
925                 anew->an_desc = NULL;
926                 anew->an_oc = NULL;
927                 anew->an_oc_exclude = 0;
928                 ber_str2bv(s, 0, 1, &anew->an_name);
929                 slap_bv2ad(&anew->an_name, &anew->an_desc, &text);
930                 if ( !anew->an_desc ) {
931                         switch( anew->an_name.bv_val[0] ) {
932                         case '-': {
933                                         struct berval adname;
934                                         adname.bv_len = anew->an_name.bv_len - 1;
935                                         adname.bv_val = &anew->an_name.bv_val[1];
936                                         slap_bv2ad(&adname, &anew->an_desc, &text);
937                                         if ( !anew->an_desc ) {
938                                                 goto reterr;
939                                         }
940                                 } break;
941
942                         case '@':
943                         case '+': /* (deprecated) */
944                         case '!': {
945                                         struct berval ocname;
946                                         ocname.bv_len = anew->an_name.bv_len - 1;
947                                         ocname.bv_val = &anew->an_name.bv_val[1];
948                                         anew->an_oc = oc_bvfind( &ocname );
949                                         if ( !anew->an_oc ) {
950                                                 goto reterr;
951                                         }
952
953                                         if ( anew->an_name.bv_val[0] == '!' ) {
954                                                 anew->an_oc_exclude = 1;
955                                         }
956                                 } break;
957
958                         default:
959                                 /* old (deprecated) way */
960                                 anew->an_oc = oc_bvfind( &anew->an_name );
961                                 if ( !anew->an_oc ) {
962                                         goto reterr;
963                                 }
964                         }
965                 }
966                 anew++;
967         }
968
969         BER_BVZERO( &anew->an_name );
970         free( str );
971         return( an );
972
973 reterr:
974         anlist_free( an, 1, NULL );
975
976         /*
977          * overwrites input string
978          * on error!
979          */
980         strcpy( in, s );
981         free( str );
982         return NULL;
983 }
984
985 void
986 anlist_free( AttributeName *an, int freename, void *ctx )
987 {
988         if ( an == NULL ) {
989                 return;
990         }
991
992         if ( freename ) {
993                 int     i;
994
995                 for ( i = 0; an[i].an_name.bv_val; i++ ) {
996                         ber_memfree_x( an[i].an_name.bv_val, ctx );
997                 }
998         }
999
1000         ber_memfree_x( an, ctx );
1001 }
1002
1003 char **anlist2charray_x( AttributeName *an, int dup, void *ctx )
1004 {
1005     char **attrs;
1006     int i;
1007                                                                                 
1008     if ( an != NULL ) {
1009         for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ )
1010             ;
1011                 attrs = (char **) slap_sl_malloc( (i + 1) * sizeof(char *), ctx );
1012         for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ ) {
1013                         if ( dup )
1014                     attrs[i] = ch_strdup( an[i].an_name.bv_val );
1015                         else
1016                     attrs[i] = an[i].an_name.bv_val;
1017         }
1018         attrs[i] = NULL;
1019     } else {
1020         attrs = NULL;
1021     }
1022                                                                                 
1023     return attrs;
1024 }
1025
1026 char **anlist2charray( AttributeName *an, int dup )
1027 {
1028         return anlist2charray_x( an, dup, NULL );
1029 }
1030
1031 char**
1032 anlist2attrs( AttributeName * anlist )
1033 {
1034         int i, j, k = 0;
1035         int n;
1036         char **attrs;
1037         ObjectClass *oc;
1038
1039         if ( anlist == NULL )
1040                 return NULL;
1041
1042         for ( i = 0; anlist[i].an_name.bv_val; i++ ) {
1043                 if ( ( oc = anlist[i].an_oc ) ) {
1044                         for ( j = 0; oc->soc_required && oc->soc_required[j]; j++ ) ;
1045                         k += j;
1046                         for ( j = 0; oc->soc_allowed && oc->soc_allowed[j]; j++ ) ;
1047                         k += j;
1048                 }
1049         }
1050
1051         if ( i == 0 )
1052                 return NULL;
1053                                                                                 
1054         attrs = anlist2charray( anlist, 1 );
1055                                                                                 
1056         n = i;
1057                                                                                 
1058         if ( k )
1059                 attrs = (char **) ch_realloc( attrs, (i + k + 1) * sizeof( char * ));
1060
1061         for ( i = 0; anlist[i].an_name.bv_val; i++ ) {
1062                 if ( ( oc = anlist[i].an_oc ) ) {
1063                         for ( j = 0; oc->soc_required && oc->soc_required[j]; j++ ) {
1064                                 attrs[n++] = ch_strdup(
1065                                                                 oc->soc_required[j]->sat_cname.bv_val );
1066                         }
1067                         for ( j = 0; oc->soc_allowed && oc->soc_allowed[j]; j++ ) {
1068                                 attrs[n++] = ch_strdup(
1069                                                                 oc->soc_allowed[j]->sat_cname.bv_val );
1070                         }
1071                 }
1072         }
1073         
1074         if ( attrs )
1075                 attrs[n] = NULL;
1076
1077         i = 0;
1078         while ( attrs && attrs[i] ) {
1079                 if ( *attrs[i] == '@' ) {
1080                         ch_free( attrs[i] );
1081                         for ( j = i; attrs[j]; j++ ) {
1082                                 attrs[j] = attrs[j+1];
1083                         }
1084                 } else {
1085                         i++;
1086                 }
1087         }
1088
1089         for ( i = 0; attrs && attrs[i]; i++ ) {
1090                 j = i + 1;
1091                 while ( attrs && attrs[j] ) {
1092                         if ( !strcmp( attrs[i], attrs[j] )) {
1093                                 ch_free( attrs[j] );
1094                                 for ( k = j; attrs && attrs[k]; k++ ) {
1095                                         attrs[k] = attrs[k+1];
1096                                 }
1097                         } else {
1098                                 j++;
1099                         }
1100                 }
1101         }
1102
1103         if ( i != n )
1104                 attrs = (char **) ch_realloc( attrs, (i+1) * sizeof( char * ));
1105
1106         return attrs;
1107 }
1108
1109 #define LBUFSIZ 80
1110 AttributeName*
1111 file2anlist( AttributeName *an, const char *fname, const char *brkstr )
1112 {
1113         FILE    *fp;
1114         char    *line = NULL;
1115         char    *lcur = NULL;
1116         char    *c;
1117         size_t  lmax = LBUFSIZ;
1118
1119         fp = fopen( fname, "r" );
1120         if ( fp == NULL ) {
1121                 Debug( LDAP_DEBUG_ANY,
1122                         "get_attrs_from_file: failed to open attribute list file "
1123                         "\"%s\": %s\n", fname, strerror(errno), 0 );
1124                 return NULL;
1125         }
1126
1127         lcur = line = (char *) ch_malloc( lmax );
1128         if ( !line ) {
1129                 Debug( LDAP_DEBUG_ANY,
1130                         "get_attrs_from_file: could not allocate memory\n",
1131                         0, 0, 0 );
1132                 fclose(fp);
1133                 return NULL;
1134         }
1135
1136         while ( fgets( lcur, LBUFSIZ, fp ) != NULL ) {
1137                 if ( ( c = strchr( lcur, '\n' ) ) ) {
1138                         if ( c == line ) {
1139                                 *c = '\0';
1140                         } else if ( *(c-1) == '\r' ) {
1141                                 *(c-1) = '\0';
1142                         } else {
1143                                 *c = '\0';
1144                         }
1145                 } else {
1146                         lmax += LBUFSIZ;
1147                         line = (char *) ch_realloc( line, lmax );
1148                         if ( !line ) {
1149                                 Debug( LDAP_DEBUG_ANY,
1150                                         "get_attrs_from_file: could not allocate memory\n",
1151                                         0, 0, 0 );
1152                                 fclose(fp);
1153                                 return NULL;
1154                         }
1155                         lcur = line + strlen( line );
1156                         continue;
1157                 }
1158                 an = str2anlist( an, line, brkstr );
1159                 if ( an == NULL )
1160                         break;
1161                 lcur = line;
1162         }
1163         ch_free( line );
1164         fclose(fp);
1165         return an;
1166 }
1167 #undef LBUFSIZ
1168
1169 /* Define an attribute option. */
1170 int
1171 ad_define_option( const char *name, const char *fname, int lineno )
1172 {
1173         int i;
1174         unsigned int optlen;
1175
1176         if ( options == &lang_option ) {
1177                 options = NULL;
1178                 option_count = 0;
1179         }
1180         if ( name == NULL )
1181                 return 0;
1182
1183         optlen = 0;
1184         do {
1185                 if ( !DESC_CHAR( name[optlen] ) ) {
1186                         /* allow trailing '=', same as '-' */
1187                         if ( name[optlen] == '=' && !name[optlen+1] ) {
1188                                 msad_range_hack = 1;
1189                                 continue;
1190                         }
1191                         Debug( LDAP_DEBUG_ANY,
1192                                "%s: line %d: illegal option name \"%s\"\n",
1193                                     fname, lineno, name );
1194                         return 1;
1195                 }
1196         } while ( name[++optlen] );
1197
1198         options = ch_realloc( options,
1199                 (option_count+1) * sizeof(Attr_option) );
1200
1201         if ( strcasecmp( name, "binary" ) == 0
1202              || ad_find_option_definition( name, optlen ) ) {
1203                 Debug( LDAP_DEBUG_ANY,
1204                        "%s: line %d: option \"%s\" is already defined\n",
1205                        fname, lineno, name );
1206                 return 1;
1207         }
1208
1209         for ( i = option_count; i; --i ) {
1210                 if ( strcasecmp( name, options[i-1].name.bv_val ) >= 0 )
1211                         break;
1212                 options[i] = options[i-1];
1213         }
1214
1215         options[i].name.bv_val = ch_strdup( name );
1216         options[i].name.bv_len = optlen;
1217         options[i].prefix = (name[optlen-1] == '-') ||
1218                 (name[optlen-1] == '=');
1219
1220         if ( i != option_count &&
1221              options[i].prefix &&
1222              optlen < options[i+1].name.bv_len &&
1223              strncasecmp( name, options[i+1].name.bv_val, optlen ) == 0 ) {
1224                         Debug( LDAP_DEBUG_ANY,
1225                                "%s: line %d: option \"%s\" overrides previous option\n",
1226                                     fname, lineno, name );
1227                         return 1;
1228         }
1229
1230         option_count++;
1231         return 0;
1232 }
1233
1234 void
1235 ad_unparse_options( BerVarray *res )
1236 {
1237         int i;
1238         for ( i = 0; i < option_count; i++ ) {
1239                 value_add_one( res, &options[i].name );
1240         }
1241 }
1242
1243 /* Find the definition of the option name or prefix matching the arguments */
1244 static Attr_option *
1245 ad_find_option_definition( const char *opt, int optlen )
1246 {
1247         int top = 0, bot = option_count;
1248         while ( top < bot ) {
1249                 int mid = (top + bot) / 2;
1250                 int mlen = options[mid].name.bv_len;
1251                 char *mname = options[mid].name.bv_val;
1252                 int j;
1253                 if ( optlen < mlen ) {
1254                         j = strncasecmp( opt, mname, optlen ) - 1;
1255                 } else {
1256                         j = strncasecmp( opt, mname, mlen );
1257                         if ( j==0 && (optlen==mlen || options[mid].prefix) )
1258                                 return &options[mid];
1259                 }
1260                 if ( j < 0 )
1261                         bot = mid;
1262                 else
1263                         top = mid + 1;
1264         }
1265         return NULL;
1266 }
1267
1268 MatchingRule *ad_mr(
1269         AttributeDescription *ad,
1270         unsigned usage )
1271 {
1272         switch( usage & SLAP_MR_TYPE_MASK ) {
1273         case SLAP_MR_NONE:
1274         case SLAP_MR_EQUALITY:
1275                 return ad->ad_type->sat_equality;
1276                 break;
1277         case SLAP_MR_ORDERING:
1278                 return ad->ad_type->sat_ordering;
1279                 break;
1280         case SLAP_MR_SUBSTR:
1281                 return ad->ad_type->sat_substr;
1282                 break;
1283         case SLAP_MR_EXT:
1284         default:
1285                 assert( 0 /* ad_mr: bad usage */);
1286         }
1287         return NULL;
1288 }