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