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