]> git.sur5r.net Git - openldap/blob - servers/slapd/ad.c
Happy New Year (belated)
[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-2008 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20
21 #include <ac/ctype.h>
22 #include <ac/errno.h>
23 #include <ac/socket.h>
24 #include <ac/string.h>
25 #include <ac/time.h>
26
27 #include "slap.h"
28 #include "lutil.h"
29
30 static AttributeName anlist_no_attrs[] = {
31         { BER_BVC( LDAP_NO_ATTRS ), NULL, 0, NULL },
32         { BER_BVNULL, NULL, 0, NULL }
33 };
34
35 static AttributeName anlist_all_user_attributes[] = {
36         { BER_BVC( LDAP_ALL_USER_ATTRIBUTES ), NULL, 0, NULL },
37         { BER_BVNULL, NULL, 0, NULL }
38 };
39
40 static AttributeName anlist_all_operational_attributes[] = {
41         { BER_BVC( LDAP_ALL_OPERATIONAL_ATTRIBUTES ), NULL, 0, NULL },
42         { BER_BVNULL, NULL, 0, NULL }
43 };
44
45 static AttributeName anlist_all_attributes[] = {
46         { BER_BVC( LDAP_ALL_USER_ATTRIBUTES ), NULL, 0, NULL },
47         { BER_BVC( LDAP_ALL_OPERATIONAL_ATTRIBUTES ), NULL, 0, NULL },
48         { BER_BVNULL, NULL, 0, NULL }
49 };
50
51 AttributeName *slap_anlist_no_attrs = anlist_no_attrs;
52 AttributeName *slap_anlist_all_user_attributes = anlist_all_user_attributes;
53 AttributeName *slap_anlist_all_operational_attributes = anlist_all_operational_attributes;
54 AttributeName *slap_anlist_all_attributes = anlist_all_attributes;
55
56 typedef struct Attr_option {
57         struct berval name;     /* option name or prefix */
58         int           prefix;   /* NAME is a tag and range prefix */
59 } Attr_option;
60
61 static Attr_option lang_option = { BER_BVC("lang-"), 1 };
62
63 /* Options sorted by name, and number of options */
64 static Attr_option *options = &lang_option;
65 static int option_count = 1;
66
67 static 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         desc.ad_cname = *bv;
179         desc.ad_flags = 0;
180         BER_BVZERO( &desc.ad_tags );
181         name = bv->bv_val;
182         options = ber_bvchr( bv, ';' );
183         if ( options != NULL && (unsigned) ( options - name ) < bv->bv_len ) {
184                 /* don't go past the end of the berval! */
185                 desc.ad_cname.bv_len = options - name;
186         } else {
187                 options = NULL;
188         }
189         desc.ad_type = at_bvfind( &desc.ad_cname );
190         if( desc.ad_type == NULL ) {
191                 *text = "attribute type undefined";
192                 return rtn;
193         }
194
195         if( is_at_operational( desc.ad_type ) && options != NULL ) {
196                 *text = "operational attribute with options undefined";
197                 return rtn;
198         }
199
200         /*
201          * parse options in place
202          */
203         ntags = 0;
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 == STRLENOF("binary") &&
217                         strncasecmp( opt, "binary", STRLENOF("binary") ) == 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 += 1 + STRLENOF(";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", STRLENOF("binary")) < 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 AttributeDescription *
777 slap_bv2tmp_ad(
778         struct berval *bv,
779         void *memctx )
780 {
781         AttributeDescription *ad =
782                  slap_sl_mfuncs.bmf_malloc( sizeof(AttributeDescription) +
783                         bv->bv_len + 1, memctx );
784
785         ad->ad_cname.bv_val = (char *)(ad+1);
786         strncpy( ad->ad_cname.bv_val, bv->bv_val, bv->bv_len+1 );
787         ad->ad_cname.bv_len = bv->bv_len;
788         ad->ad_flags = SLAP_DESC_TEMPORARY;
789         ad->ad_type = slap_schema.si_at_undefined;
790
791         return ad;
792 }
793
794 static int
795 undef_promote(
796         AttributeType   *at,
797         char            *name,
798         AttributeType   *nat )
799 {
800         AttributeDescription    **u_ad, **n_ad;
801
802         /* Get to last ad on the new type */
803         for ( n_ad = &nat->sat_ad; *n_ad; n_ad = &(*n_ad)->ad_next ) ;
804
805         for ( u_ad = &at->sat_ad; *u_ad; ) {
806                 struct berval   bv;
807
808                 ber_str2bv( name, 0, 0, &bv );
809
810                 /* remove iff undef == name or undef == name;tag */
811                 if ( (*u_ad)->ad_cname.bv_len >= bv.bv_len
812                         && strncasecmp( (*u_ad)->ad_cname.bv_val, bv.bv_val, bv.bv_len ) == 0
813                         && ( (*u_ad)->ad_cname.bv_val[ bv.bv_len ] == '\0'
814                                 || (*u_ad)->ad_cname.bv_val[ bv.bv_len ] == ';' ) )
815                 {
816                         AttributeDescription    *tmp = *u_ad;
817
818                         *u_ad = (*u_ad)->ad_next;
819
820                         tmp->ad_next = NULL;
821                         *n_ad = tmp;
822                         n_ad = &tmp->ad_next;
823                 } else {
824                         u_ad = &(*u_ad)->ad_next;
825                 }
826         }
827
828         return 0;
829 }
830
831 int
832 slap_ad_undef_promote(
833         char *name,
834         AttributeType *at )
835 {
836         int     rc;
837
838         ldap_pvt_thread_mutex_lock( &ad_undef_mutex );
839
840         rc = undef_promote( slap_schema.si_at_undefined, name, at );
841         if ( rc == 0 ) {
842                 rc = undef_promote( slap_schema.si_at_proxied, name, at );
843         }
844
845         ldap_pvt_thread_mutex_unlock( &ad_undef_mutex );
846
847         return rc;
848 }
849
850 int
851 an_find(
852     AttributeName *a,
853     struct berval *s
854 )
855 {
856         if( a == NULL ) return 0;
857
858         for ( ; a->an_name.bv_val; a++ ) {
859                 if ( a->an_name.bv_len != s->bv_len) continue;
860                 if ( strcasecmp( s->bv_val, a->an_name.bv_val ) == 0 ) {
861                         return( 1 );
862                 }
863         }
864
865         return( 0 );
866 }
867
868 /*
869  * Convert a delimited string into a list of AttributeNames; add
870  * on to an existing list if it was given.  If the string is not
871  * a valid attribute name, if a '-' is prepended it is skipped
872  * and the remaining name is tried again; if a '@' (or '+') is
873  * prepended, an objectclass name is searched instead; if a '!'
874  * is prepended, the objectclass name is negated.
875  * 
876  * NOTE: currently, if a valid attribute name is not found, the
877  * same string is also checked as valid objectclass name; however,
878  * this behavior is deprecated.
879  */
880 AttributeName *
881 str2anlist( AttributeName *an, char *in, const char *brkstr )
882 {
883         char    *str;
884         char    *s;
885         char    *lasts;
886         int     i, j;
887         const char *text;
888         AttributeName *anew;
889
890         /* find last element in list */
891         i = 0;
892         if ( an != NULL ) {
893                 for ( i = 0; !BER_BVISNULL( &an[ i ].an_name ) ; i++)
894                         ;
895         }
896         
897         /* protect the input string from strtok */
898         str = ch_strdup( in );
899
900         /* Count words in string */
901         j = 1;
902         for ( s = str; *s; s++ ) {
903                 if ( strchr( brkstr, *s ) != NULL ) {
904                         j++;
905                 }
906         }
907
908         an = ch_realloc( an, ( i + j + 1 ) * sizeof( AttributeName ) );
909         anew = an + i;
910         for ( s = ldap_pvt_strtok( str, brkstr, &lasts );
911                 s != NULL;
912                 s = ldap_pvt_strtok( NULL, brkstr, &lasts ) )
913         {
914                 /* put a stop mark */
915                 BER_BVZERO( &anew[1].an_name );
916
917                 anew->an_desc = NULL;
918                 anew->an_oc = NULL;
919                 anew->an_oc_exclude = 0;
920                 ber_str2bv(s, 0, 1, &anew->an_name);
921                 slap_bv2ad(&anew->an_name, &anew->an_desc, &text);
922                 if ( !anew->an_desc ) {
923                         switch( anew->an_name.bv_val[0] ) {
924                         case '-': {
925                                         struct berval adname;
926                                         adname.bv_len = anew->an_name.bv_len - 1;
927                                         adname.bv_val = &anew->an_name.bv_val[1];
928                                         slap_bv2ad(&adname, &anew->an_desc, &text);
929                                         if ( !anew->an_desc ) {
930                                                 goto reterr;
931                                         }
932                                 } break;
933
934                         case '@':
935                         case '+': /* (deprecated) */
936                         case '!': {
937                                         struct berval ocname;
938                                         ocname.bv_len = anew->an_name.bv_len - 1;
939                                         ocname.bv_val = &anew->an_name.bv_val[1];
940                                         anew->an_oc = oc_bvfind( &ocname );
941                                         if ( !anew->an_oc ) {
942                                                 goto reterr;
943                                         }
944
945                                         if ( anew->an_name.bv_val[0] == '!' ) {
946                                                 anew->an_oc_exclude = 1;
947                                         }
948                                 } break;
949
950                         default:
951                                 /* old (deprecated) way */
952                                 anew->an_oc = oc_bvfind( &anew->an_name );
953                                 if ( !anew->an_oc ) {
954                                         goto reterr;
955                                 }
956                         }
957                 }
958                 anew++;
959         }
960
961         BER_BVZERO( &anew->an_name );
962         free( str );
963         return( an );
964
965 reterr:
966         anlist_free( an, 1, NULL );
967
968         /*
969          * overwrites input string
970          * on error!
971          */
972         strcpy( in, s );
973         free( str );
974         return NULL;
975 }
976
977 void
978 anlist_free( AttributeName *an, int freename, void *ctx )
979 {
980         if ( an == NULL ) {
981                 return;
982         }
983
984         if ( freename ) {
985                 int     i;
986
987                 for ( i = 0; an[i].an_name.bv_val; i++ ) {
988                         ber_memfree_x( an[i].an_name.bv_val, ctx );
989                 }
990         }
991
992         ber_memfree_x( an, ctx );
993 }
994
995 char **anlist2charray_x( AttributeName *an, int dup, void *ctx )
996 {
997     char **attrs;
998     int i;
999                                                                                 
1000     if ( an != NULL ) {
1001         for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ )
1002             ;
1003                 attrs = (char **) slap_sl_malloc( (i + 1) * sizeof(char *), ctx );
1004         for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ ) {
1005                         if ( dup )
1006                     attrs[i] = ch_strdup( an[i].an_name.bv_val );
1007                         else
1008                     attrs[i] = an[i].an_name.bv_val;
1009         }
1010         attrs[i] = NULL;
1011     } else {
1012         attrs = NULL;
1013     }
1014                                                                                 
1015     return attrs;
1016 }
1017
1018 char **anlist2charray( AttributeName *an, int dup )
1019 {
1020         return anlist2charray_x( an, dup, NULL );
1021 }
1022
1023 char**
1024 anlist2attrs( AttributeName * anlist )
1025 {
1026         int i, j, k = 0;
1027         int n;
1028         char **attrs;
1029         ObjectClass *oc;
1030
1031         if ( anlist == NULL )
1032                 return NULL;
1033
1034         for ( i = 0; anlist[i].an_name.bv_val; i++ ) {
1035                 if ( ( oc = anlist[i].an_oc ) ) {
1036                         for ( j = 0; oc->soc_required && oc->soc_required[j]; j++ ) ;
1037                         k += j;
1038                         for ( j = 0; oc->soc_allowed && oc->soc_allowed[j]; j++ ) ;
1039                         k += j;
1040                 }
1041         }
1042
1043         if ( i == 0 )
1044                 return NULL;
1045                                                                                 
1046         attrs = anlist2charray( anlist, 1 );
1047                                                                                 
1048         n = i;
1049                                                                                 
1050         if ( k )
1051                 attrs = (char **) ch_realloc( attrs, (i + k + 1) * sizeof( char * ));
1052
1053         for ( i = 0; anlist[i].an_name.bv_val; i++ ) {
1054                 if ( ( oc = anlist[i].an_oc ) ) {
1055                         for ( j = 0; oc->soc_required && oc->soc_required[j]; j++ ) {
1056                                 attrs[n++] = ch_strdup(
1057                                                                 oc->soc_required[j]->sat_cname.bv_val );
1058                         }
1059                         for ( j = 0; oc->soc_allowed && oc->soc_allowed[j]; j++ ) {
1060                                 attrs[n++] = ch_strdup(
1061                                                                 oc->soc_allowed[j]->sat_cname.bv_val );
1062                         }
1063                 }
1064         }
1065         
1066         if ( attrs )
1067                 attrs[n] = NULL;
1068
1069         i = 0;
1070         while ( attrs && attrs[i] ) {
1071                 if ( *attrs[i] == '@' ) {
1072                         ch_free( attrs[i] );
1073                         for ( j = i; attrs[j]; j++ ) {
1074                                 attrs[j] = attrs[j+1];
1075                         }
1076                 } else {
1077                         i++;
1078                 }
1079         }
1080
1081         for ( i = 0; attrs && attrs[i]; i++ ) {
1082                 j = i + 1;
1083                 while ( attrs && attrs[j] ) {
1084                         if ( !strcmp( attrs[i], attrs[j] )) {
1085                                 ch_free( attrs[j] );
1086                                 for ( k = j; attrs && attrs[k]; k++ ) {
1087                                         attrs[k] = attrs[k+1];
1088                                 }
1089                         } else {
1090                                 j++;
1091                         }
1092                 }
1093         }
1094
1095         if ( i != n )
1096                 attrs = (char **) ch_realloc( attrs, (i+1) * sizeof( char * ));
1097
1098         return attrs;
1099 }
1100
1101 #define LBUFSIZ 80
1102 AttributeName*
1103 file2anlist( AttributeName *an, const char *fname, const char *brkstr )
1104 {
1105         FILE    *fp;
1106         char    *line = NULL;
1107         char    *lcur = NULL;
1108         char    *c;
1109         size_t  lmax = LBUFSIZ;
1110
1111         fp = fopen( fname, "r" );
1112         if ( fp == NULL ) {
1113                 Debug( LDAP_DEBUG_ANY,
1114                         "get_attrs_from_file: failed to open attribute list file "
1115                         "\"%s\": %s\n", fname, strerror(errno), 0 );
1116                 return NULL;
1117         }
1118
1119         lcur = line = (char *) ch_malloc( lmax );
1120         if ( !line ) {
1121                 Debug( LDAP_DEBUG_ANY,
1122                         "get_attrs_from_file: could not allocate memory\n",
1123                         0, 0, 0 );
1124                 fclose(fp);
1125                 return NULL;
1126         }
1127
1128         while ( fgets( lcur, LBUFSIZ, fp ) != NULL ) {
1129                 if ( ( c = strchr( lcur, '\n' ) ) ) {
1130                         if ( c == line ) {
1131                                 *c = '\0';
1132                         } else if ( *(c-1) == '\r' ) {
1133                                 *(c-1) = '\0';
1134                         } else {
1135                                 *c = '\0';
1136                         }
1137                 } else {
1138                         lmax += LBUFSIZ;
1139                         line = (char *) ch_realloc( line, lmax );
1140                         if ( !line ) {
1141                                 Debug( LDAP_DEBUG_ANY,
1142                                         "get_attrs_from_file: could not allocate memory\n",
1143                                         0, 0, 0 );
1144                                 fclose(fp);
1145                                 return NULL;
1146                         }
1147                         lcur = line + strlen( line );
1148                         continue;
1149                 }
1150                 an = str2anlist( an, line, brkstr );
1151                 if ( an == NULL )
1152                         break;
1153                 lcur = line;
1154         }
1155         ch_free( line );
1156         fclose(fp);
1157         return an;
1158 }
1159 #undef LBUFSIZ
1160
1161 /* Define an attribute option. */
1162 int
1163 ad_define_option( const char *name, const char *fname, int lineno )
1164 {
1165         int i;
1166         unsigned int optlen;
1167
1168         if ( options == &lang_option ) {
1169                 options = NULL;
1170                 option_count = 0;
1171         }
1172         if ( name == NULL )
1173                 return 0;
1174
1175         optlen = 0;
1176         do {
1177                 if ( !DESC_CHAR( name[optlen] ) ) {
1178                         Debug( LDAP_DEBUG_ANY,
1179                                "%s: line %d: illegal option name \"%s\"\n",
1180                                     fname, lineno, name );
1181                         return 1;
1182                 }
1183         } while ( name[++optlen] );
1184
1185         options = ch_realloc( options,
1186                 (option_count+1) * sizeof(Attr_option) );
1187
1188         if ( strcasecmp( name, "binary" ) == 0
1189              || ad_find_option_definition( name, optlen ) ) {
1190                 Debug( LDAP_DEBUG_ANY,
1191                        "%s: line %d: option \"%s\" is already defined\n",
1192                        fname, lineno, name );
1193                 return 1;
1194         }
1195
1196         for ( i = option_count; i; --i ) {
1197                 if ( strcasecmp( name, options[i-1].name.bv_val ) >= 0 )
1198                         break;
1199                 options[i] = options[i-1];
1200         }
1201
1202         options[i].name.bv_val = ch_strdup( name );
1203         options[i].name.bv_len = optlen;
1204         options[i].prefix = (name[optlen-1] == '-');
1205
1206         if ( i != option_count &&
1207              options[i].prefix &&
1208              optlen < options[i+1].name.bv_len &&
1209              strncasecmp( name, options[i+1].name.bv_val, optlen ) == 0 ) {
1210                         Debug( LDAP_DEBUG_ANY,
1211                                "%s: line %d: option \"%s\" overrides previous option\n",
1212                                     fname, lineno, name );
1213                         return 1;
1214         }
1215
1216         option_count++;
1217         return 0;
1218 }
1219
1220 void
1221 ad_unparse_options( BerVarray *res )
1222 {
1223         int i;
1224         for ( i = 0; i < option_count; i++ ) {
1225                 value_add_one( res, &options[i].name );
1226         }
1227 }
1228
1229 /* Find the definition of the option name or prefix matching the arguments */
1230 static Attr_option *
1231 ad_find_option_definition( const char *opt, int optlen )
1232 {
1233         int top = 0, bot = option_count;
1234         while ( top < bot ) {
1235                 int mid = (top + bot) / 2;
1236                 int mlen = options[mid].name.bv_len;
1237                 char *mname = options[mid].name.bv_val;
1238                 int j;
1239                 if ( optlen < mlen ) {
1240                         j = strncasecmp( opt, mname, optlen ) - 1;
1241                 } else {
1242                         j = strncasecmp( opt, mname, mlen );
1243                         if ( j==0 && (optlen==mlen || options[mid].prefix) )
1244                                 return &options[mid];
1245                 }
1246                 if ( j < 0 )
1247                         bot = mid;
1248                 else
1249                         top = mid + 1;
1250         }
1251         return NULL;
1252 }
1253
1254 MatchingRule *ad_mr(
1255         AttributeDescription *ad,
1256         unsigned usage )
1257 {
1258         switch( usage & SLAP_MR_TYPE_MASK ) {
1259         case SLAP_MR_NONE:
1260         case SLAP_MR_EQUALITY:
1261                 return ad->ad_type->sat_equality;
1262                 break;
1263         case SLAP_MR_ORDERING:
1264                 return ad->ad_type->sat_ordering;
1265                 break;
1266         case SLAP_MR_SUBSTR:
1267                 return ad->ad_type->sat_substr;
1268                 break;
1269         case SLAP_MR_EXT:
1270         default:
1271                 assert( 0 /* ad_mr: bad usage */);
1272         }
1273         return NULL;
1274 }