]> git.sur5r.net Git - openldap/blob - libraries/libldap/schema.c
4639b4510520425b654205641064776db02f9b1d
[openldap] / libraries / libldap / schema.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2004 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15
16 /*
17  * schema.c:  parsing routines used by servers and clients to process
18  *      schema definitions
19  */
20
21 #include "portable.h"
22
23 #include <stdio.h>
24 #include <ac/stdlib.h>
25
26 #include <ac/string.h>
27 #include <ac/time.h>
28
29 #include "ldap-int.h"
30
31 #include <ldap_schema.h>
32
33 static const char *
34 choose_name( char *names[], const char *fallback )
35 {
36         return (names != NULL && names[0] != NULL) ? names[0] : fallback;
37 }
38
39 LDAP_CONST char *
40 ldap_syntax2name( LDAPSyntax * syn )
41 {
42         return( syn->syn_oid );
43 }
44
45 LDAP_CONST char *
46 ldap_matchingrule2name( LDAPMatchingRule * mr )
47 {
48         return( choose_name( mr->mr_names, mr->mr_oid ) );
49 }
50
51 LDAP_CONST char *
52 ldap_matchingruleuse2name( LDAPMatchingRuleUse * mru )
53 {
54         return( choose_name( mru->mru_names, mru->mru_oid ) );
55 }
56
57 LDAP_CONST char *
58 ldap_attributetype2name( LDAPAttributeType * at )
59 {
60         return( choose_name( at->at_names, at->at_oid ) );
61 }
62
63 LDAP_CONST char *
64 ldap_objectclass2name( LDAPObjectClass * oc )
65 {
66         return( choose_name( oc->oc_names, oc->oc_oid ) );
67 }
68
69 LDAP_CONST char *
70 ldap_contentrule2name( LDAPContentRule * cr )
71 {
72         return( choose_name( cr->cr_names, cr->cr_oid ) );
73 }
74
75 LDAP_CONST char *
76 ldap_nameform2name( LDAPNameForm * nf )
77 {
78         return( choose_name( nf->nf_names, nf->nf_oid ) );
79 }
80
81 LDAP_CONST char *
82 ldap_structurerule2name( LDAPStructureRule * sr )
83 {
84         return( choose_name( sr->sr_names, NULL ) );
85 }
86
87 /*
88  * When pretty printing the entities we will be appending to a buffer.
89  * Since checking for overflow, realloc'ing and checking if no error
90  * is extremely boring, we will use a protection layer that will let
91  * us blissfully ignore the error until the end.  This layer is
92  * implemented with the help of the next type.
93  */
94
95 typedef struct safe_string {
96         char * val;
97         ber_len_t size;
98         ber_len_t pos;
99         int at_whsp;
100 } safe_string;
101
102 static safe_string *
103 new_safe_string(int size)
104 {
105         safe_string * ss;
106         
107         ss = LDAP_MALLOC(sizeof(safe_string));
108         if ( !ss )
109                 return(NULL);
110
111         ss->val = LDAP_MALLOC(size);
112         if ( !ss->val ) {
113                 LDAP_FREE(ss);
114                 return(NULL);
115         }
116
117         ss->size = size;
118         ss->pos = 0;
119         ss->at_whsp = 0;
120
121         return ss;
122 }
123
124 static void
125 safe_string_free(safe_string * ss)
126 {
127         if ( !ss )
128                 return;
129         LDAP_FREE(ss->val);
130         LDAP_FREE(ss);
131 }
132
133 #if 0   /* unused */
134 static char *
135 safe_string_val(safe_string * ss)
136 {
137         ss->val[ss->pos] = '\0';
138         return(ss->val);
139 }
140 #endif
141
142 static char *
143 safe_strdup(safe_string * ss)
144 {
145         char *ret = LDAP_MALLOC(ss->pos+1);
146         if (!ret)
147                 return NULL;
148         AC_MEMCPY(ret, ss->val, ss->pos);
149         ret[ss->pos] = '\0';
150         return ret;
151 }
152
153 static int
154 append_to_safe_string(safe_string * ss, char * s)
155 {
156         int l = strlen(s);
157         char * temp;
158
159         /*
160          * Some runaway process is trying to append to a string that
161          * overflowed and we could not extend.
162          */
163         if ( !ss->val )
164                 return -1;
165
166         /* We always make sure there is at least one position available */
167         if ( ss->pos + l >= ss->size-1 ) {
168                 ss->size *= 2;
169                 if ( ss->pos + l >= ss->size-1 ) {
170                         ss->size = ss->pos + l + 1;
171                 }
172
173                 temp = LDAP_REALLOC(ss->val, ss->size);
174                 if ( !temp ) {
175                         /* Trouble, out of memory */
176                         LDAP_FREE(ss->val);
177                         return -1;
178                 }
179                 ss->val = temp;
180         }
181         strncpy(&ss->val[ss->pos], s, l);
182         ss->pos += l;
183         if ( ss->pos > 0 && LDAP_SPACE(ss->val[ss->pos-1]) )
184                 ss->at_whsp = 1;
185         else
186                 ss->at_whsp = 0;
187
188         return 0;
189 }
190
191 static int
192 print_literal(safe_string *ss, char *s)
193 {
194         return(append_to_safe_string(ss,s));
195 }
196
197 static int
198 print_whsp(safe_string *ss)
199 {
200         if ( ss->at_whsp )
201                 return(append_to_safe_string(ss,""));
202         else
203                 return(append_to_safe_string(ss," "));
204 }
205
206 static int
207 print_numericoid(safe_string *ss, char *s)
208 {
209         if ( s )
210                 return(append_to_safe_string(ss,s));
211         else
212                 return(append_to_safe_string(ss,""));
213 }
214
215 /* This one is identical to print_qdescr */
216 static int
217 print_qdstring(safe_string *ss, char *s)
218 {
219         print_whsp(ss);
220         print_literal(ss,"'");
221         append_to_safe_string(ss,s);
222         print_literal(ss,"'");
223         return(print_whsp(ss));
224 }
225
226 static int
227 print_qdescr(safe_string *ss, char *s)
228 {
229         print_whsp(ss);
230         print_literal(ss,"'");
231         append_to_safe_string(ss,s);
232         print_literal(ss,"'");
233         return(print_whsp(ss));
234 }
235
236 static int
237 print_qdescrlist(safe_string *ss, char **sa)
238 {
239         char **sp;
240         int ret = 0;
241         
242         for (sp=sa; *sp; sp++) {
243                 ret = print_qdescr(ss,*sp);
244         }
245         /* If the list was empty, we return zero that is potentially
246          * incorrect, but since we will be still appending things, the
247          * overflow will be detected later.  Maybe FIX.
248          */
249         return(ret);
250 }
251
252 static int
253 print_qdescrs(safe_string *ss, char **sa)
254 {
255         /* The only way to represent an empty list is as a qdescrlist
256          * so, if the list is empty we treat it as a long list.
257          * Really, this is what the syntax mandates.  We should not
258          * be here if the list was empty, but if it happens, a label
259          * has already been output and we cannot undo it.
260          */
261         if ( !sa[0] || ( sa[0] && sa[1] ) ) {
262                 print_whsp(ss);
263                 print_literal(ss,"("/*)*/);
264                 print_qdescrlist(ss,sa);
265                 print_literal(ss,/*(*/")");
266                 return(print_whsp(ss));
267         } else {
268           return(print_qdescr(ss,*sa));
269         }
270 }
271
272 static int
273 print_woid(safe_string *ss, char *s)
274 {
275         print_whsp(ss);
276         append_to_safe_string(ss,s);
277         return print_whsp(ss);
278 }
279
280 static int
281 print_oidlist(safe_string *ss, char **sa)
282 {
283         char **sp;
284
285         for (sp=sa; *(sp+1); sp++) {
286                 print_woid(ss,*sp);
287                 print_literal(ss,"$");
288         }
289         return(print_woid(ss,*sp));
290 }
291
292 static int
293 print_oids(safe_string *ss, char **sa)
294 {
295         if ( sa[0] && sa[1] ) {
296                 print_literal(ss,"("/*)*/);
297                 print_oidlist(ss,sa);
298                 print_whsp(ss);
299                 return(print_literal(ss,/*(*/")"));
300         } else {
301                 return(print_woid(ss,*sa));
302         }
303 }
304
305 static int
306 print_noidlen(safe_string *ss, char *s, int l)
307 {
308         char buf[64];
309         int ret;
310
311         ret = print_numericoid(ss,s);
312         if ( l ) {
313                 snprintf(buf, sizeof buf, "{%d}",l);
314                 ret = print_literal(ss,buf);
315         }
316         return(ret);
317 }
318
319 static int
320 print_ruleid(safe_string *ss, int rid)
321 {
322         char buf[64];
323         snprintf(buf, sizeof buf, "%d", rid);
324         return print_literal(ss,buf);
325 }
326
327 static int
328 print_ruleids(safe_string *ss, int n, int *rids)
329 {
330         int i;
331
332         if( n == 1 ) {
333                 print_ruleid(ss,rids[0]);
334                 return print_whsp(ss);
335         } else {
336                 print_literal(ss,"("/*)*/);
337                 for( i=0; i<n; i++ ) {
338                         print_whsp(ss);
339                         print_ruleid(ss,rids[i]);
340                 }
341                 print_whsp(ss);
342                 return print_literal(ss,/*(*/")");
343         }
344 }
345
346
347 static int
348 print_extensions(safe_string *ss, LDAPSchemaExtensionItem **extensions)
349 {
350         LDAPSchemaExtensionItem **ext;
351
352         if ( extensions ) {
353                 print_whsp(ss);
354                 for ( ext = extensions; *ext != NULL; ext++ ) {
355                         print_literal(ss, (*ext)->lsei_name);
356                         print_whsp(ss);
357                         /* Should be print_qdstrings */
358                         print_qdescrs(ss, (*ext)->lsei_values);
359                         print_whsp(ss);
360                 }
361         }
362
363         return 0;
364 }
365
366 char *
367 ldap_syntax2str( LDAPSyntax * syn )
368 {
369         struct berval bv;
370         if (ldap_syntax2bv( syn, &bv ))
371                 return(bv.bv_val);
372         else
373                 return NULL;
374 }
375
376 struct berval *
377 ldap_syntax2bv( LDAPSyntax * syn, struct berval *bv )
378 {
379         safe_string * ss;
380         
381         ss = new_safe_string(256);
382         if ( !ss )
383                 return NULL;
384
385         print_literal(ss,"("/*)*/);
386         print_whsp(ss);
387
388         print_numericoid(ss, syn->syn_oid);
389         print_whsp(ss);
390
391         if ( syn->syn_desc ) {
392                 print_literal(ss,"DESC");
393                 print_qdstring(ss,syn->syn_desc);
394         }
395
396         print_whsp(ss);
397
398         print_extensions(ss, syn->syn_extensions);
399
400         print_literal(ss,/*(*/ ")");
401
402         bv->bv_val = safe_strdup(ss);
403         bv->bv_len = ss->pos;
404         safe_string_free(ss);
405         return(bv);
406 }
407
408 char *
409 ldap_matchingrule2str( LDAPMatchingRule * mr )
410 {
411         struct berval bv;
412         if (ldap_matchingrule2bv( mr, &bv ))
413                 return(bv.bv_val);
414         else
415                 return NULL;
416 }
417
418 struct berval *
419 ldap_matchingrule2bv( LDAPMatchingRule * mr, struct berval *bv )
420 {
421         safe_string * ss;
422         
423         ss = new_safe_string(256);
424         if ( !ss )
425                 return NULL;
426
427         print_literal(ss,"(" /*)*/);
428         print_whsp(ss);
429
430         print_numericoid(ss, mr->mr_oid);
431         print_whsp(ss);
432
433         if ( mr->mr_names ) {
434                 print_literal(ss,"NAME");
435                 print_qdescrs(ss,mr->mr_names);
436         }
437
438         if ( mr->mr_desc ) {
439                 print_literal(ss,"DESC");
440                 print_qdstring(ss,mr->mr_desc);
441         }
442
443         if ( mr->mr_obsolete ) {
444                 print_literal(ss, "OBSOLETE");
445                 print_whsp(ss);
446         }
447
448         if ( mr->mr_syntax_oid ) {
449                 print_literal(ss,"SYNTAX");
450                 print_whsp(ss);
451                 print_literal(ss, mr->mr_syntax_oid);
452                 print_whsp(ss);
453         }
454
455         print_whsp(ss);
456
457         print_extensions(ss, mr->mr_extensions);
458
459         print_literal(ss,/*(*/")");
460
461         bv->bv_val = safe_strdup(ss);
462         bv->bv_len = ss->pos;
463         safe_string_free(ss);
464         return(bv);
465 }
466
467 char *
468 ldap_matchingruleuse2str( LDAPMatchingRuleUse * mru )
469 {
470         struct berval bv;
471         if (ldap_matchingruleuse2bv( mru, &bv ))
472                 return(bv.bv_val);
473         else
474                 return NULL;
475 }
476
477 struct berval *
478 ldap_matchingruleuse2bv( LDAPMatchingRuleUse * mru, struct berval *bv )
479 {
480         safe_string * ss;
481         
482         ss = new_safe_string(256);
483         if ( !ss )
484                 return NULL;
485
486         print_literal(ss,"(" /*)*/);
487         print_whsp(ss);
488
489         print_numericoid(ss, mru->mru_oid);
490         print_whsp(ss);
491
492         if ( mru->mru_names ) {
493                 print_literal(ss,"NAME");
494                 print_qdescrs(ss,mru->mru_names);
495         }
496
497         if ( mru->mru_desc ) {
498                 print_literal(ss,"DESC");
499                 print_qdstring(ss,mru->mru_desc);
500         }
501
502         if ( mru->mru_obsolete ) {
503                 print_literal(ss, "OBSOLETE");
504                 print_whsp(ss);
505         }
506
507         if ( mru->mru_applies_oids ) {
508                 print_literal(ss,"APPLIES");
509                 print_whsp(ss);
510                 print_oids(ss, mru->mru_applies_oids);
511                 print_whsp(ss);
512         }
513
514         print_whsp(ss);
515
516         print_extensions(ss, mru->mru_extensions);
517
518         print_literal(ss,/*(*/")");
519
520         bv->bv_val = safe_strdup(ss);
521         bv->bv_len = ss->pos;
522         safe_string_free(ss);
523         return(bv);
524 }
525
526 char *
527 ldap_objectclass2str( LDAPObjectClass * oc )
528 {
529         struct berval bv;
530         if (ldap_objectclass2bv( oc, &bv ))
531                 return(bv.bv_val);
532         else
533                 return NULL;
534 }
535
536 struct berval *
537 ldap_objectclass2bv( LDAPObjectClass * oc, struct berval *bv )
538 {
539         safe_string * ss;
540         
541         ss = new_safe_string(256);
542         if ( !ss )
543                 return NULL;
544
545         print_literal(ss,"("/*)*/);
546         print_whsp(ss);
547
548         print_numericoid(ss, oc->oc_oid);
549         print_whsp(ss);
550
551         if ( oc->oc_names ) {
552                 print_literal(ss,"NAME");
553                 print_qdescrs(ss,oc->oc_names);
554         }
555
556         if ( oc->oc_desc ) {
557                 print_literal(ss,"DESC");
558                 print_qdstring(ss,oc->oc_desc);
559         }
560
561         if ( oc->oc_obsolete ) {
562                 print_literal(ss, "OBSOLETE");
563                 print_whsp(ss);
564         }
565
566         if ( oc->oc_sup_oids ) {
567                 print_literal(ss,"SUP");
568                 print_whsp(ss);
569                 print_oids(ss,oc->oc_sup_oids);
570                 print_whsp(ss);
571         }
572
573         switch (oc->oc_kind) {
574         case LDAP_SCHEMA_ABSTRACT:
575                 print_literal(ss,"ABSTRACT");
576                 break;
577         case LDAP_SCHEMA_STRUCTURAL:
578                 print_literal(ss,"STRUCTURAL");
579                 break;
580         case LDAP_SCHEMA_AUXILIARY:
581                 print_literal(ss,"AUXILIARY");
582                 break;
583         default:
584                 print_literal(ss,"KIND-UNKNOWN");
585                 break;
586         }
587         print_whsp(ss);
588         
589         if ( oc->oc_at_oids_must ) {
590                 print_literal(ss,"MUST");
591                 print_whsp(ss);
592                 print_oids(ss,oc->oc_at_oids_must);
593                 print_whsp(ss);
594         }
595
596         if ( oc->oc_at_oids_may ) {
597                 print_literal(ss,"MAY");
598                 print_whsp(ss);
599                 print_oids(ss,oc->oc_at_oids_may);
600                 print_whsp(ss);
601         }
602
603         print_whsp(ss);
604
605         print_extensions(ss, oc->oc_extensions);
606
607         print_literal(ss, /*(*/")");
608
609         bv->bv_val = safe_strdup(ss);
610         bv->bv_len = ss->pos;
611         safe_string_free(ss);
612         return(bv);
613 }
614
615 char *
616 ldap_contentrule2str( LDAPContentRule * cr )
617 {
618         struct berval bv;
619         if (ldap_contentrule2bv( cr, &bv ))
620                 return(bv.bv_val);
621         else
622                 return NULL;
623 }
624
625 struct berval *
626 ldap_contentrule2bv( LDAPContentRule * cr, struct berval *bv )
627 {
628         safe_string * ss;
629         
630         ss = new_safe_string(256);
631         if ( !ss )
632                 return NULL;
633
634         print_literal(ss,"("/*)*/);
635         print_whsp(ss);
636
637         print_numericoid(ss, cr->cr_oid);
638         print_whsp(ss);
639
640         if ( cr->cr_names ) {
641                 print_literal(ss,"NAME");
642                 print_qdescrs(ss,cr->cr_names);
643         }
644
645         if ( cr->cr_desc ) {
646                 print_literal(ss,"DESC");
647                 print_qdstring(ss,cr->cr_desc);
648         }
649
650         if ( cr->cr_obsolete ) {
651                 print_literal(ss, "OBSOLETE");
652                 print_whsp(ss);
653         }
654
655         if ( cr->cr_oc_oids_aux ) {
656                 print_literal(ss,"AUX");
657                 print_whsp(ss);
658                 print_oids(ss,cr->cr_oc_oids_aux);
659                 print_whsp(ss);
660         }
661
662         if ( cr->cr_at_oids_must ) {
663                 print_literal(ss,"MUST");
664                 print_whsp(ss);
665                 print_oids(ss,cr->cr_at_oids_must);
666                 print_whsp(ss);
667         }
668
669         if ( cr->cr_at_oids_may ) {
670                 print_literal(ss,"MAY");
671                 print_whsp(ss);
672                 print_oids(ss,cr->cr_at_oids_may);
673                 print_whsp(ss);
674         }
675
676         if ( cr->cr_at_oids_not ) {
677                 print_literal(ss,"NOT");
678                 print_whsp(ss);
679                 print_oids(ss,cr->cr_at_oids_not);
680                 print_whsp(ss);
681         }
682
683         print_whsp(ss);
684         print_extensions(ss, cr->cr_extensions);
685
686         print_literal(ss, /*(*/")");
687
688         bv->bv_val = safe_strdup(ss);
689         bv->bv_len = ss->pos;
690         safe_string_free(ss);
691         return(bv);
692 }
693
694 char *
695 ldap_structurerule2str( LDAPStructureRule * sr )
696 {
697         struct berval bv;
698         if (ldap_structurerule2bv( sr, &bv ))
699                 return(bv.bv_val);
700         else
701                 return NULL;
702 }
703
704 struct berval *
705 ldap_structurerule2bv( LDAPStructureRule * sr, struct berval *bv )
706 {
707         safe_string * ss;
708         
709         ss = new_safe_string(256);
710         if ( !ss )
711                 return NULL;
712
713         print_literal(ss,"("/*)*/);
714         print_whsp(ss);
715
716         print_ruleid(ss, sr->sr_ruleid);
717         print_whsp(ss);
718
719         if ( sr->sr_names ) {
720                 print_literal(ss,"NAME");
721                 print_qdescrs(ss,sr->sr_names);
722         }
723
724         if ( sr->sr_desc ) {
725                 print_literal(ss,"DESC");
726                 print_qdstring(ss,sr->sr_desc);
727         }
728
729         if ( sr->sr_obsolete ) {
730                 print_literal(ss, "OBSOLETE");
731                 print_whsp(ss);
732         }
733
734         print_literal(ss,"FORM");
735         print_whsp(ss);
736         print_woid(ss,sr->sr_nameform);
737         print_whsp(ss);
738
739         if ( sr->sr_nsup_ruleids ) {
740                 print_literal(ss,"SUP");
741                 print_whsp(ss);
742                 print_ruleids(ss,sr->sr_nsup_ruleids,sr->sr_sup_ruleids);
743                 print_whsp(ss);
744         }
745
746         print_whsp(ss);
747         print_extensions(ss, sr->sr_extensions);
748
749         print_literal(ss, /*(*/")");
750
751         bv->bv_val = safe_strdup(ss);
752         bv->bv_len = ss->pos;
753         safe_string_free(ss);
754         return(bv);
755 }
756
757
758 char *
759 ldap_nameform2str( LDAPNameForm * nf )
760 {
761         struct berval bv;
762         if (ldap_nameform2bv( nf, &bv ))
763                 return(bv.bv_val);
764         else
765                 return NULL;
766 }
767
768 struct berval *
769 ldap_nameform2bv( LDAPNameForm * nf, struct berval *bv )
770 {
771         safe_string * ss;
772         
773         ss = new_safe_string(256);
774         if ( !ss )
775                 return NULL;
776
777         print_literal(ss,"("/*)*/);
778         print_whsp(ss);
779
780         print_numericoid(ss, nf->nf_oid);
781         print_whsp(ss);
782
783         if ( nf->nf_names ) {
784                 print_literal(ss,"NAME");
785                 print_qdescrs(ss,nf->nf_names);
786         }
787
788         if ( nf->nf_desc ) {
789                 print_literal(ss,"DESC");
790                 print_qdstring(ss,nf->nf_desc);
791         }
792
793         if ( nf->nf_obsolete ) {
794                 print_literal(ss, "OBSOLETE");
795                 print_whsp(ss);
796         }
797
798         print_literal(ss,"OC");
799         print_whsp(ss);
800         print_woid(ss,nf->nf_objectclass);
801         print_whsp(ss);
802
803         print_literal(ss,"MUST");
804         print_whsp(ss);
805         print_oids(ss,nf->nf_at_oids_must);
806         print_whsp(ss);
807
808
809         if ( nf->nf_at_oids_may ) {
810                 print_literal(ss,"MAY");
811                 print_whsp(ss);
812                 print_oids(ss,nf->nf_at_oids_may);
813                 print_whsp(ss);
814         }
815
816         print_whsp(ss);
817         print_extensions(ss, nf->nf_extensions);
818
819         print_literal(ss, /*(*/")");
820
821         bv->bv_val = safe_strdup(ss);
822         bv->bv_len = ss->pos;
823         safe_string_free(ss);
824         return(bv);
825 }
826
827 char *
828 ldap_attributetype2str( LDAPAttributeType * at )
829 {
830         struct berval bv;
831         if (ldap_attributetype2bv( at, &bv ))
832                 return(bv.bv_val);
833         else
834                 return NULL;
835 }
836
837 struct berval *
838 ldap_attributetype2bv(  LDAPAttributeType * at, struct berval *bv )
839 {
840         safe_string * ss;
841         
842         ss = new_safe_string(256);
843         if ( !ss )
844                 return NULL;
845
846         print_literal(ss,"("/*)*/);
847         print_whsp(ss);
848
849         print_numericoid(ss, at->at_oid);
850         print_whsp(ss);
851
852         if ( at->at_names ) {
853                 print_literal(ss,"NAME");
854                 print_qdescrs(ss,at->at_names);
855         }
856
857         if ( at->at_desc ) {
858                 print_literal(ss,"DESC");
859                 print_qdstring(ss,at->at_desc);
860         }
861
862         if ( at->at_obsolete ) {
863                 print_literal(ss, "OBSOLETE");
864                 print_whsp(ss);
865         }
866
867         if ( at->at_sup_oid ) {
868                 print_literal(ss,"SUP");
869                 print_woid(ss,at->at_sup_oid);
870         }
871
872         if ( at->at_equality_oid ) {
873                 print_literal(ss,"EQUALITY");
874                 print_woid(ss,at->at_equality_oid);
875         }
876
877         if ( at->at_ordering_oid ) {
878                 print_literal(ss,"ORDERING");
879                 print_woid(ss,at->at_ordering_oid);
880         }
881
882         if ( at->at_substr_oid ) {
883                 print_literal(ss,"SUBSTR");
884                 print_woid(ss,at->at_substr_oid);
885         }
886
887         if ( at->at_syntax_oid ) {
888                 print_literal(ss,"SYNTAX");
889                 print_whsp(ss);
890                 print_noidlen(ss,at->at_syntax_oid,at->at_syntax_len);
891                 print_whsp(ss);
892         }
893
894         if ( at->at_single_value == LDAP_SCHEMA_YES ) {
895                 print_literal(ss,"SINGLE-VALUE");
896                 print_whsp(ss);
897         }
898
899         if ( at->at_collective == LDAP_SCHEMA_YES ) {
900                 print_literal(ss,"COLLECTIVE");
901                 print_whsp(ss);
902         }
903
904         if ( at->at_no_user_mod == LDAP_SCHEMA_YES ) {
905                 print_literal(ss,"NO-USER-MODIFICATION");
906                 print_whsp(ss);
907         }
908
909         if ( at->at_usage != LDAP_SCHEMA_USER_APPLICATIONS ) {
910                 print_literal(ss,"USAGE");
911                 print_whsp(ss);
912                 switch (at->at_usage) {
913                 case LDAP_SCHEMA_DIRECTORY_OPERATION:
914                         print_literal(ss,"directoryOperation");
915                         break;
916                 case LDAP_SCHEMA_DISTRIBUTED_OPERATION:
917                         print_literal(ss,"distributedOperation");
918                         break;
919                 case LDAP_SCHEMA_DSA_OPERATION:
920                         print_literal(ss,"dSAOperation");
921                         break;
922                 default:
923                         print_literal(ss,"UNKNOWN");
924                         break;
925                 }
926         }
927         
928         print_whsp(ss);
929
930         print_extensions(ss, at->at_extensions);
931
932         print_literal(ss,/*(*/")");
933
934         bv->bv_val = safe_strdup(ss);
935         bv->bv_len = ss->pos;
936         safe_string_free(ss);
937         return(bv);
938 }
939
940 /*
941  * Now come the parsers.  There is one parser for each entity type:
942  * objectclasses, attributetypes, etc.
943  *
944  * Each of them is written as a recursive-descent parser, except that
945  * none of them is really recursive.  But the idea is kept: there
946  * is one routine per non-terminal that eithers gobbles lexical tokens
947  * or calls lower-level routines, etc.
948  *
949  * The scanner is implemented in the routine get_token.  Actually,
950  * get_token is more than a scanner and will return tokens that are
951  * in fact non-terminals in the grammar.  So you can see the whole
952  * approach as the combination of a low-level bottom-up recognizer
953  * combined with a scanner and a number of top-down parsers.  Or just
954  * consider that the real grammars recognized by the parsers are not
955  * those of the standards.  As a matter of fact, our parsers are more
956  * liberal than the spec when there is no ambiguity.
957  *
958  * The difference is pretty academic (modulo bugs or incorrect
959  * interpretation of the specs).
960  */
961
962 #define TK_NOENDQUOTE   -2
963 #define TK_OUTOFMEM     -1
964 #define TK_EOS          0
965 #define TK_UNEXPCHAR    1
966 #define TK_BAREWORD     2
967 #define TK_QDSTRING     3
968 #define TK_LEFTPAREN    4
969 #define TK_RIGHTPAREN   5
970 #define TK_DOLLAR       6
971 #define TK_QDESCR       TK_QDSTRING
972
973 struct token {
974         int type;
975         char *sval;
976 };
977
978 static int
979 get_token( const char ** sp, char ** token_val )
980 {
981         int kind;
982         const char * p;
983         const char * q;
984         char * res;
985
986         *token_val = NULL;
987         switch (**sp) {
988         case '\0':
989                 kind = TK_EOS;
990                 (*sp)++;
991                 break;
992         case '(':
993                 kind = TK_LEFTPAREN;
994                 (*sp)++;
995                 break;
996         case ')':
997                 kind = TK_RIGHTPAREN;
998                 (*sp)++;
999                 break;
1000         case '$':
1001                 kind = TK_DOLLAR;
1002                 (*sp)++;
1003                 break;
1004         case '\'':
1005                 kind = TK_QDSTRING;
1006                 (*sp)++;
1007                 p = *sp;
1008                 while ( **sp != '\'' && **sp != '\0' )
1009                         (*sp)++;
1010                 if ( **sp == '\'' ) {
1011                         q = *sp;
1012                         res = LDAP_MALLOC(q-p+1);
1013                         if ( !res ) {
1014                                 kind = TK_OUTOFMEM;
1015                         } else {
1016                                 strncpy(res,p,q-p);
1017                                 res[q-p] = '\0';
1018                                 *token_val = res;
1019                         }
1020                         (*sp)++;
1021                 } else {
1022                         kind = TK_NOENDQUOTE;
1023                 }
1024                 break;
1025         default:
1026                 kind = TK_BAREWORD;
1027                 p = *sp;
1028                 while ( !LDAP_SPACE(**sp) &&
1029                         **sp != '(' &&
1030                         **sp != ')' &&
1031                         **sp != '$' &&
1032                         **sp != '\'' &&
1033                         **sp != '\0' )
1034                         (*sp)++;
1035                 q = *sp;
1036                 res = LDAP_MALLOC(q-p+1);
1037                 if ( !res ) {
1038                         kind = TK_OUTOFMEM;
1039                 } else {
1040                         strncpy(res,p,q-p);
1041                         res[q-p] = '\0';
1042                         *token_val = res;
1043                 }
1044                 break;
1045 /*              kind = TK_UNEXPCHAR; */
1046 /*              break; */
1047         }
1048         
1049         return kind;
1050 }
1051
1052 /* Gobble optional whitespace */
1053 static void
1054 parse_whsp(const char **sp)
1055 {
1056         while (LDAP_SPACE(**sp))
1057                 (*sp)++;
1058 }
1059
1060 /* TBC:!!
1061  * General note for all parsers: to guarantee the algorithm halts they
1062  * must always advance the pointer even when an error is found.  For
1063  * this one is not that important since an error here is fatal at the
1064  * upper layers, but it is a simple strategy that will not get in
1065  * endless loops.
1066  */
1067
1068 /* Parse a sequence of dot-separated decimal strings */
1069 char *
1070 ldap_int_parse_numericoid(const char **sp, int *code, const int flags)
1071 {
1072         char * res = NULL;
1073         const char * start = *sp;
1074         int len;
1075         int quoted = 0;
1076
1077         /* Netscape puts the SYNTAX value in quotes (incorrectly) */
1078         if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && **sp == '\'' ) {
1079                 quoted = 1;
1080                 (*sp)++;
1081                 start++;
1082         }
1083         /* Each iteration of this loop gets one decimal string */
1084         while (**sp) {
1085                 if ( !LDAP_DIGIT(**sp) ) {
1086                         /*
1087                          * Initial char is not a digit or char after dot is
1088                          * not a digit
1089                          */
1090                         *code = LDAP_SCHERR_NODIGIT;
1091                         return NULL;
1092                 }
1093                 (*sp)++;
1094                 while ( LDAP_DIGIT(**sp) )
1095                         (*sp)++;
1096                 if ( **sp != '.' )
1097                         break;
1098                 /* Otherwise, gobble the dot and loop again */
1099                 (*sp)++;
1100         }
1101         /* Now *sp points at the char past the numericoid. Perfect. */
1102         len = *sp - start;
1103         if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && quoted ) {
1104                 if ( **sp == '\'' ) {
1105                         (*sp)++;
1106                 } else {
1107                         *code = LDAP_SCHERR_UNEXPTOKEN;
1108                         return NULL;
1109                 }
1110         }
1111         if (flags & LDAP_SCHEMA_SKIP) {
1112                 res = (char *)start;
1113         } else {
1114                 res = LDAP_MALLOC(len+1);
1115                 if (!res) {
1116                         *code = LDAP_SCHERR_OUTOFMEM;
1117                         return(NULL);
1118                 }
1119                 strncpy(res,start,len);
1120                 res[len] = '\0';
1121         }
1122         return(res);
1123 }
1124
1125 /* Parse a sequence of dot-separated decimal strings */
1126 int
1127 ldap_int_parse_ruleid(const char **sp, int *code, const int flags, int *ruleid)
1128 {
1129         *ruleid=0;
1130
1131         if ( !LDAP_DIGIT(**sp) ) {
1132                 *code = LDAP_SCHERR_NODIGIT;
1133                 return -1;
1134         }
1135         *ruleid = (**sp) - '0';
1136         (*sp)++;
1137
1138         while ( LDAP_DIGIT(**sp) ) {
1139                 *ruleid *= 10;
1140                 *ruleid += (**sp) - '0';
1141                 (*sp)++;
1142         }
1143
1144         return 0;
1145 }
1146
1147 /* Parse a qdescr or a list of them enclosed in () */
1148 static char **
1149 parse_qdescrs(const char **sp, int *code)
1150 {
1151         char ** res;
1152         char ** res1;
1153         int kind;
1154         char * sval;
1155         int size;
1156         int pos;
1157
1158         parse_whsp(sp);
1159         kind = get_token(sp,&sval);
1160         if ( kind == TK_LEFTPAREN ) {
1161                 /* Let's presume there will be at least 2 entries */
1162                 size = 3;
1163                 res = LDAP_CALLOC(3,sizeof(char *));
1164                 if ( !res ) {
1165                         *code = LDAP_SCHERR_OUTOFMEM;
1166                         return NULL;
1167                 }
1168                 pos = 0;
1169                 while (1) {
1170                         parse_whsp(sp);
1171                         kind = get_token(sp,&sval);
1172                         if ( kind == TK_RIGHTPAREN )
1173                                 break;
1174                         if ( kind == TK_QDESCR ) {
1175                                 if ( pos == size-2 ) {
1176                                         size++;
1177                                         res1 = LDAP_REALLOC(res,size*sizeof(char *));
1178                                         if ( !res1 ) {
1179                                                 LDAP_VFREE(res);
1180                                                 LDAP_FREE(sval);
1181                                                 *code = LDAP_SCHERR_OUTOFMEM;
1182                                                 return(NULL);
1183                                         }
1184                                         res = res1;
1185                                 }
1186                                 res[pos++] = sval;
1187                                 res[pos] = NULL;
1188                                 parse_whsp(sp);
1189                         } else {
1190                                 LDAP_VFREE(res);
1191                                 LDAP_FREE(sval);
1192                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1193                                 return(NULL);
1194                         }
1195                 }
1196                 parse_whsp(sp);
1197                 return(res);
1198         } else if ( kind == TK_QDESCR ) {
1199                 res = LDAP_CALLOC(2,sizeof(char *));
1200                 if ( !res ) {
1201                         *code = LDAP_SCHERR_OUTOFMEM;
1202                         return NULL;
1203                 }
1204                 res[0] = sval;
1205                 res[1] = NULL;
1206                 parse_whsp(sp);
1207                 return res;
1208         } else {
1209                 LDAP_FREE(sval);
1210                 *code = LDAP_SCHERR_BADNAME;
1211                 return NULL;
1212         }
1213 }
1214
1215 /* Parse a woid */
1216 static char *
1217 parse_woid(const char **sp, int *code)
1218 {
1219         char * sval;
1220         int kind;
1221
1222         parse_whsp(sp);
1223         kind = get_token(sp, &sval);
1224         if ( kind != TK_BAREWORD ) {
1225                 LDAP_FREE(sval);
1226                 *code = LDAP_SCHERR_UNEXPTOKEN;
1227                 return NULL;
1228         }
1229         parse_whsp(sp);
1230         return sval;
1231 }
1232
1233 /* Parse a noidlen */
1234 static char *
1235 parse_noidlen(const char **sp, int *code, int *len, int allow_quoted)
1236 {
1237         char * sval;
1238         int quoted = 0;
1239
1240         *len = 0;
1241         /* Netscape puts the SYNTAX value in quotes (incorrectly) */
1242         if ( allow_quoted && **sp == '\'' ) {
1243                 quoted = 1;
1244                 (*sp)++;
1245         }
1246         sval = ldap_int_parse_numericoid(sp, code, 0);
1247         if ( !sval ) {
1248                 return NULL;
1249         }
1250         if ( **sp == '{' /*}*/ ) {
1251                 (*sp)++;
1252                 *len = atoi(*sp);
1253                 while ( LDAP_DIGIT(**sp) )
1254                         (*sp)++;
1255                 if ( **sp != /*{*/ '}' ) {
1256                         *code = LDAP_SCHERR_UNEXPTOKEN;
1257                         LDAP_FREE(sval);
1258                         return NULL;
1259                 }
1260                 (*sp)++;
1261         }               
1262         if ( allow_quoted && quoted ) {
1263                 if ( **sp == '\'' ) {
1264                         (*sp)++;
1265                 } else {
1266                         *code = LDAP_SCHERR_UNEXPTOKEN;
1267                         LDAP_FREE(sval);
1268                         return NULL;
1269                 }
1270         }
1271         return sval;
1272 }
1273
1274 /*
1275  * Next routine will accept a qdstring in place of an oid if
1276  * allow_quoted is set.  This is necessary to interoperate with
1277  * Netscape Directory server that will improperly quote each oid (at
1278  * least those of the descr kind) in the SUP clause.
1279  */
1280
1281 /* Parse a woid or a $-separated list of them enclosed in () */
1282 static char **
1283 parse_oids(const char **sp, int *code, const int allow_quoted)
1284 {
1285         char ** res;
1286         char ** res1;
1287         int kind;
1288         char * sval;
1289         int size;
1290         int pos;
1291
1292         /*
1293          * Strictly speaking, doing this here accepts whsp before the
1294          * ( at the begining of an oidlist, but this is harmless.  Also,
1295          * we are very liberal in what we accept as an OID.  Maybe
1296          * refine later.
1297          */
1298         parse_whsp(sp);
1299         kind = get_token(sp,&sval);
1300         if ( kind == TK_LEFTPAREN ) {
1301                 /* Let's presume there will be at least 2 entries */
1302                 size = 3;
1303                 res = LDAP_CALLOC(3,sizeof(char *));
1304                 if ( !res ) {
1305                         *code = LDAP_SCHERR_OUTOFMEM;
1306                         return NULL;
1307                 }
1308                 pos = 0;
1309                 parse_whsp(sp);
1310                 kind = get_token(sp,&sval);
1311                 if ( kind == TK_BAREWORD ||
1312                      ( allow_quoted && kind == TK_QDSTRING ) ) {
1313                         res[pos++] = sval;
1314                         res[pos] = NULL;
1315                 } else {
1316                         *code = LDAP_SCHERR_UNEXPTOKEN;
1317                         LDAP_FREE(sval);
1318                         LDAP_VFREE(res);
1319                         return NULL;
1320                 }
1321                 parse_whsp(sp);
1322                 while (1) {
1323                         kind = get_token(sp,&sval);
1324                         if ( kind == TK_RIGHTPAREN )
1325                                 break;
1326                         if ( kind == TK_DOLLAR ) {
1327                                 parse_whsp(sp);
1328                                 kind = get_token(sp,&sval);
1329                                 if ( kind == TK_BAREWORD ||
1330                                      ( allow_quoted &&
1331                                        kind == TK_QDSTRING ) ) {
1332                                         if ( pos == size-2 ) {
1333                                                 size++;
1334                                                 res1 = LDAP_REALLOC(res,size*sizeof(char *));
1335                                                 if ( !res1 ) {
1336                                                         LDAP_FREE(sval);
1337                                                         LDAP_VFREE(res);
1338                                                         *code = LDAP_SCHERR_OUTOFMEM;
1339                                                         return(NULL);
1340                                                 }
1341                                                 res = res1;
1342                                         }
1343                                         res[pos++] = sval;
1344                                         res[pos] = NULL;
1345                                 } else {
1346                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1347                                         LDAP_FREE(sval);
1348                                         LDAP_VFREE(res);
1349                                         return NULL;
1350                                 }
1351                                 parse_whsp(sp);
1352                         } else {
1353                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1354                                 LDAP_FREE(sval);
1355                                 LDAP_VFREE(res);
1356                                 return NULL;
1357                         }
1358                 }
1359                 parse_whsp(sp);
1360                 return(res);
1361         } else if ( kind == TK_BAREWORD ||
1362                     ( allow_quoted && kind == TK_QDSTRING ) ) {
1363                 res = LDAP_CALLOC(2,sizeof(char *));
1364                 if ( !res ) {
1365                         LDAP_FREE(sval);
1366                         *code = LDAP_SCHERR_OUTOFMEM;
1367                         return NULL;
1368                 }
1369                 res[0] = sval;
1370                 res[1] = NULL;
1371                 parse_whsp(sp);
1372                 return res;
1373         } else {
1374                 LDAP_FREE(sval);
1375                 *code = LDAP_SCHERR_BADNAME;
1376                 return NULL;
1377         }
1378 }
1379
1380 static int
1381 add_extension(LDAPSchemaExtensionItem ***extensions,
1382               char * name, char ** values)
1383 {
1384         int n;
1385         LDAPSchemaExtensionItem **tmp, *ext;
1386
1387         ext = LDAP_CALLOC(1, sizeof(LDAPSchemaExtensionItem));
1388         if ( !ext )
1389                 return 1;
1390         ext->lsei_name = name;
1391         ext->lsei_values = values;
1392
1393         if ( !*extensions ) {
1394                 *extensions =
1395                   LDAP_CALLOC(2, sizeof(LDAPSchemaExtensionItem *));
1396                 if ( !*extensions )
1397                   return 1;
1398                 n = 0;
1399         } else {
1400                 for ( n=0; (*extensions)[n] != NULL; n++ )
1401                         ;
1402                 tmp = LDAP_REALLOC(*extensions,
1403                                    (n+2)*sizeof(LDAPSchemaExtensionItem *));
1404                 if ( !tmp )
1405                         return 1;
1406                 *extensions = tmp;
1407         }
1408         (*extensions)[n] = ext;
1409         (*extensions)[n+1] = NULL;
1410         return 0;
1411 }
1412
1413 static void
1414 free_extensions(LDAPSchemaExtensionItem **extensions)
1415 {
1416         LDAPSchemaExtensionItem **ext;
1417
1418         if ( extensions ) {
1419                 for ( ext = extensions; *ext != NULL; ext++ ) {
1420                         LDAP_FREE((*ext)->lsei_name);
1421                         LDAP_VFREE((*ext)->lsei_values);
1422                         LDAP_FREE(*ext);
1423                 }
1424                 LDAP_FREE(extensions);
1425         }
1426 }
1427
1428 void
1429 ldap_syntax_free( LDAPSyntax * syn )
1430 {
1431         LDAP_FREE(syn->syn_oid);
1432         if (syn->syn_names) LDAP_VFREE(syn->syn_names);
1433         if (syn->syn_desc) LDAP_FREE(syn->syn_desc);
1434         free_extensions(syn->syn_extensions);
1435         LDAP_FREE(syn);
1436 }
1437
1438 LDAPSyntax *
1439 ldap_str2syntax( LDAP_CONST char * s,
1440         int * code,
1441         LDAP_CONST char ** errp,
1442         LDAP_CONST unsigned flags )
1443 {
1444         int kind;
1445         const char * ss = s;
1446         char * sval;
1447         int seen_name = 0;
1448         int seen_desc = 0;
1449         LDAPSyntax * syn;
1450         char ** ext_vals;
1451
1452         if ( !s ) {
1453                 *code = LDAP_SCHERR_EMPTY;
1454                 *errp = "";
1455                 return NULL;
1456         }
1457
1458         *errp = s;
1459         syn = LDAP_CALLOC(1,sizeof(LDAPSyntax));
1460
1461         if ( !syn ) {
1462                 *code = LDAP_SCHERR_OUTOFMEM;
1463                 return NULL;
1464         }
1465
1466         kind = get_token(&ss,&sval);
1467         if ( kind != TK_LEFTPAREN ) {
1468                 LDAP_FREE(sval);
1469                 *code = LDAP_SCHERR_NOLEFTPAREN;
1470                 ldap_syntax_free(syn);
1471                 return NULL;
1472         }
1473
1474         parse_whsp(&ss);
1475         syn->syn_oid = ldap_int_parse_numericoid(&ss,code,0);
1476         if ( !syn->syn_oid ) {
1477                 *errp = ss;
1478                 ldap_syntax_free(syn);
1479                 return NULL;
1480         }
1481         parse_whsp(&ss);
1482
1483         /*
1484          * Beyond this point we will be liberal and accept the items
1485          * in any order.
1486          */
1487         while (1) {
1488                 kind = get_token(&ss,&sval);
1489                 switch (kind) {
1490                 case TK_EOS:
1491                         *code = LDAP_SCHERR_NORIGHTPAREN;
1492                         *errp = ss;
1493                         ldap_syntax_free(syn);
1494                         return NULL;
1495                 case TK_RIGHTPAREN:
1496                         return syn;
1497                 case TK_BAREWORD:
1498                         if ( !strcasecmp(sval,"NAME") ) {
1499                                 LDAP_FREE(sval);
1500                                 if ( seen_name ) {
1501                                         *code = LDAP_SCHERR_DUPOPT;
1502                                         *errp = ss;
1503                                         ldap_syntax_free(syn);
1504                                         return(NULL);
1505                                 }
1506                                 seen_name = 1;
1507                                 syn->syn_names = parse_qdescrs(&ss,code);
1508                                 if ( !syn->syn_names ) {
1509                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1510                                                 *code = LDAP_SCHERR_BADNAME;
1511                                         *errp = ss;
1512                                         ldap_syntax_free(syn);
1513                                         return NULL;
1514                                 }
1515                         } else if ( !strcasecmp(sval,"DESC") ) {
1516                                 LDAP_FREE(sval);
1517                                 if ( seen_desc ) {
1518                                         *code = LDAP_SCHERR_DUPOPT;
1519                                         *errp = ss;
1520                                         ldap_syntax_free(syn);
1521                                         return(NULL);
1522                                 }
1523                                 seen_desc = 1;
1524                                 parse_whsp(&ss);
1525                                 kind = get_token(&ss,&sval);
1526                                 if ( kind != TK_QDSTRING ) {
1527                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1528                                         *errp = ss;
1529                                         LDAP_FREE(sval);
1530                                         ldap_syntax_free(syn);
1531                                         return NULL;
1532                                 }
1533                                 syn->syn_desc = sval;
1534                                 parse_whsp(&ss);
1535                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1536                                 /* Should be parse_qdstrings */
1537                                 ext_vals = parse_qdescrs(&ss, code);
1538                                 if ( !ext_vals ) {
1539                                         *errp = ss;
1540                                         ldap_syntax_free(syn);
1541                                         return NULL;
1542                                 }
1543                                 if ( add_extension(&syn->syn_extensions,
1544                                                     sval, ext_vals) ) {
1545                                         *code = LDAP_SCHERR_OUTOFMEM;
1546                                         *errp = ss;
1547                                         LDAP_FREE(sval);
1548                                         ldap_syntax_free(syn);
1549                                         return NULL;
1550                                 }
1551                         } else {
1552                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1553                                 *errp = ss;
1554                                 LDAP_FREE(sval);
1555                                 ldap_syntax_free(syn);
1556                                 return NULL;
1557                         }
1558                         break;
1559                 default:
1560                         *code = LDAP_SCHERR_UNEXPTOKEN;
1561                         *errp = ss;
1562                         LDAP_FREE(sval);
1563                         ldap_syntax_free(syn);
1564                         return NULL;
1565                 }
1566         }
1567 }
1568
1569 void
1570 ldap_matchingrule_free( LDAPMatchingRule * mr )
1571 {
1572         LDAP_FREE(mr->mr_oid);
1573         if (mr->mr_names) LDAP_VFREE(mr->mr_names);
1574         if (mr->mr_desc) LDAP_FREE(mr->mr_desc);
1575         if (mr->mr_syntax_oid) LDAP_FREE(mr->mr_syntax_oid);
1576         free_extensions(mr->mr_extensions);
1577         LDAP_FREE(mr);
1578 }
1579
1580 LDAPMatchingRule *
1581 ldap_str2matchingrule( LDAP_CONST char * s,
1582         int * code,
1583         LDAP_CONST char ** errp,
1584         LDAP_CONST unsigned flags )
1585 {
1586         int kind;
1587         const char * ss = s;
1588         char * sval;
1589         int seen_name = 0;
1590         int seen_desc = 0;
1591         int seen_obsolete = 0;
1592         int seen_syntax = 0;
1593         LDAPMatchingRule * mr;
1594         char ** ext_vals;
1595         const char * savepos;
1596
1597         if ( !s ) {
1598                 *code = LDAP_SCHERR_EMPTY;
1599                 *errp = "";
1600                 return NULL;
1601         }
1602
1603         *errp = s;
1604         mr = LDAP_CALLOC(1,sizeof(LDAPMatchingRule));
1605
1606         if ( !mr ) {
1607                 *code = LDAP_SCHERR_OUTOFMEM;
1608                 return NULL;
1609         }
1610
1611         kind = get_token(&ss,&sval);
1612         if ( kind != TK_LEFTPAREN ) {
1613                 *code = LDAP_SCHERR_NOLEFTPAREN;
1614                 LDAP_FREE(sval);
1615                 ldap_matchingrule_free(mr);
1616                 return NULL;
1617         }
1618
1619         parse_whsp(&ss);
1620         savepos = ss;
1621         mr->mr_oid = ldap_int_parse_numericoid(&ss,code,flags);
1622         if ( !mr->mr_oid ) {
1623                 if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
1624                         /* Backtracking */
1625                         ss = savepos;
1626                         kind = get_token(&ss,&sval);
1627                         if ( kind == TK_BAREWORD ) {
1628                                 if ( !strcasecmp(sval, "NAME") ||
1629                                      !strcasecmp(sval, "DESC") ||
1630                                      !strcasecmp(sval, "OBSOLETE") ||
1631                                      !strcasecmp(sval, "SYNTAX") ||
1632                                      !strncasecmp(sval, "X-", 2) ) {
1633                                         /* Missing OID, backtrack */
1634                                         ss = savepos;
1635                                 } else {
1636                                         /* Non-numerical OID, ignore */
1637                                 }
1638                         }
1639                         LDAP_FREE(sval);
1640                 } else {
1641                         *errp = ss;
1642                         ldap_matchingrule_free(mr);
1643                         return NULL;
1644                 }
1645         }
1646         parse_whsp(&ss);
1647
1648         /*
1649          * Beyond this point we will be liberal and accept the items
1650          * in any order.
1651          */
1652         while (1) {
1653                 kind = get_token(&ss,&sval);
1654                 switch (kind) {
1655                 case TK_EOS:
1656                         *code = LDAP_SCHERR_NORIGHTPAREN;
1657                         *errp = ss;
1658                         ldap_matchingrule_free(mr);
1659                         return NULL;
1660                 case TK_RIGHTPAREN:
1661                         if( !seen_syntax ) {
1662                                 *code = LDAP_SCHERR_MISSING;
1663                                 ldap_matchingrule_free(mr);
1664                                 return NULL;
1665                         }
1666                         return mr;
1667                 case TK_BAREWORD:
1668                         if ( !strcasecmp(sval,"NAME") ) {
1669                                 LDAP_FREE(sval);
1670                                 if ( seen_name ) {
1671                                         *code = LDAP_SCHERR_DUPOPT;
1672                                         *errp = ss;
1673                                         ldap_matchingrule_free(mr);
1674                                         return(NULL);
1675                                 }
1676                                 seen_name = 1;
1677                                 mr->mr_names = parse_qdescrs(&ss,code);
1678                                 if ( !mr->mr_names ) {
1679                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1680                                                 *code = LDAP_SCHERR_BADNAME;
1681                                         *errp = ss;
1682                                         ldap_matchingrule_free(mr);
1683                                         return NULL;
1684                                 }
1685                         } else if ( !strcasecmp(sval,"DESC") ) {
1686                                 LDAP_FREE(sval);
1687                                 if ( seen_desc ) {
1688                                         *code = LDAP_SCHERR_DUPOPT;
1689                                         *errp = ss;
1690                                         ldap_matchingrule_free(mr);
1691                                         return(NULL);
1692                                 }
1693                                 seen_desc = 1;
1694                                 parse_whsp(&ss);
1695                                 kind = get_token(&ss,&sval);
1696                                 if ( kind != TK_QDSTRING ) {
1697                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1698                                         *errp = ss;
1699                                         LDAP_FREE(sval);
1700                                         ldap_matchingrule_free(mr);
1701                                         return NULL;
1702                                 }
1703                                 mr->mr_desc = sval;
1704                                 parse_whsp(&ss);
1705                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
1706                                 LDAP_FREE(sval);
1707                                 if ( seen_obsolete ) {
1708                                         *code = LDAP_SCHERR_DUPOPT;
1709                                         *errp = ss;
1710                                         ldap_matchingrule_free(mr);
1711                                         return(NULL);
1712                                 }
1713                                 seen_obsolete = 1;
1714                                 mr->mr_obsolete = LDAP_SCHEMA_YES;
1715                                 parse_whsp(&ss);
1716                         } else if ( !strcasecmp(sval,"SYNTAX") ) {
1717                                 LDAP_FREE(sval);
1718                                 if ( seen_syntax ) {
1719                                         *code = LDAP_SCHERR_DUPOPT;
1720                                         *errp = ss;
1721                                         ldap_matchingrule_free(mr);
1722                                         return(NULL);
1723                                 }
1724                                 seen_syntax = 1;
1725                                 parse_whsp(&ss);
1726                                 mr->mr_syntax_oid =
1727                                         ldap_int_parse_numericoid(&ss,code,flags);
1728                                 if ( !mr->mr_syntax_oid ) {
1729                                         *errp = ss;
1730                                         ldap_matchingrule_free(mr);
1731                                         return NULL;
1732                                 }
1733                                 parse_whsp(&ss);
1734                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1735                                 /* Should be parse_qdstrings */
1736                                 ext_vals = parse_qdescrs(&ss, code);
1737                                 if ( !ext_vals ) {
1738                                         *errp = ss;
1739                                         ldap_matchingrule_free(mr);
1740                                         return NULL;
1741                                 }
1742                                 if ( add_extension(&mr->mr_extensions,
1743                                                     sval, ext_vals) ) {
1744                                         *code = LDAP_SCHERR_OUTOFMEM;
1745                                         *errp = ss;
1746                                         LDAP_FREE(sval);
1747                                         ldap_matchingrule_free(mr);
1748                                         return NULL;
1749                                 }
1750                         } else {
1751                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1752                                 *errp = ss;
1753                                 LDAP_FREE(sval);
1754                                 ldap_matchingrule_free(mr);
1755                                 return NULL;
1756                         }
1757                         break;
1758                 default:
1759                         *code = LDAP_SCHERR_UNEXPTOKEN;
1760                         *errp = ss;
1761                         LDAP_FREE(sval);
1762                         ldap_matchingrule_free(mr);
1763                         return NULL;
1764                 }
1765         }
1766 }
1767
1768 void
1769 ldap_matchingruleuse_free( LDAPMatchingRuleUse * mru )
1770 {
1771         LDAP_FREE(mru->mru_oid);
1772         if (mru->mru_names) LDAP_VFREE(mru->mru_names);
1773         if (mru->mru_desc) LDAP_FREE(mru->mru_desc);
1774         if (mru->mru_applies_oids) LDAP_VFREE(mru->mru_applies_oids);
1775         free_extensions(mru->mru_extensions);
1776         LDAP_FREE(mru);
1777 }
1778
1779 LDAPMatchingRuleUse *
1780 ldap_str2matchingruleuse( LDAP_CONST char * s,
1781         int * code,
1782         LDAP_CONST char ** errp,
1783         LDAP_CONST unsigned flags )
1784 {
1785         int kind;
1786         const char * ss = s;
1787         char * sval;
1788         int seen_name = 0;
1789         int seen_desc = 0;
1790         int seen_obsolete = 0;
1791         int seen_applies = 0;
1792         LDAPMatchingRuleUse * mru;
1793         char ** ext_vals;
1794         const char * savepos;
1795
1796         if ( !s ) {
1797                 *code = LDAP_SCHERR_EMPTY;
1798                 *errp = "";
1799                 return NULL;
1800         }
1801
1802         *errp = s;
1803         mru = LDAP_CALLOC(1,sizeof(LDAPMatchingRuleUse));
1804
1805         if ( !mru ) {
1806                 *code = LDAP_SCHERR_OUTOFMEM;
1807                 return NULL;
1808         }
1809
1810         kind = get_token(&ss,&sval);
1811         if ( kind != TK_LEFTPAREN ) {
1812                 *code = LDAP_SCHERR_NOLEFTPAREN;
1813                 LDAP_FREE(sval);
1814                 ldap_matchingruleuse_free(mru);
1815                 return NULL;
1816         }
1817
1818         parse_whsp(&ss);
1819         savepos = ss;
1820         mru->mru_oid = ldap_int_parse_numericoid(&ss,code,flags);
1821         if ( !mru->mru_oid ) {
1822                 if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
1823                         /* Backtracking */
1824                         ss = savepos;
1825                         kind = get_token(&ss,&sval);
1826                         if ( kind == TK_BAREWORD ) {
1827                                 if ( !strcasecmp(sval, "NAME") ||
1828                                      !strcasecmp(sval, "DESC") ||
1829                                      !strcasecmp(sval, "OBSOLETE") ||
1830                                      !strcasecmp(sval, "APPLIES") ||
1831                                      !strncasecmp(sval, "X-", 2) ) {
1832                                         /* Missing OID, backtrack */
1833                                         ss = savepos;
1834                                 } else {
1835                                         /* Non-numerical OID, ignore */
1836                                 }
1837                         }
1838                         LDAP_FREE(sval);
1839                 } else {
1840                         *errp = ss;
1841                         ldap_matchingruleuse_free(mru);
1842                         return NULL;
1843                 }
1844         }
1845         parse_whsp(&ss);
1846
1847         /*
1848          * Beyond this point we will be liberal and accept the items
1849          * in any order.
1850          */
1851         while (1) {
1852                 kind = get_token(&ss,&sval);
1853                 switch (kind) {
1854                 case TK_EOS:
1855                         *code = LDAP_SCHERR_NORIGHTPAREN;
1856                         *errp = ss;
1857                         ldap_matchingruleuse_free(mru);
1858                         return NULL;
1859                 case TK_RIGHTPAREN:
1860                         if( !seen_applies ) {
1861                                 *code = LDAP_SCHERR_MISSING;
1862                                 ldap_matchingruleuse_free(mru);
1863                                 return NULL;
1864                         }
1865                         return mru;
1866                 case TK_BAREWORD:
1867                         if ( !strcasecmp(sval,"NAME") ) {
1868                                 LDAP_FREE(sval);
1869                                 if ( seen_name ) {
1870                                         *code = LDAP_SCHERR_DUPOPT;
1871                                         *errp = ss;
1872                                         ldap_matchingruleuse_free(mru);
1873                                         return(NULL);
1874                                 }
1875                                 seen_name = 1;
1876                                 mru->mru_names = parse_qdescrs(&ss,code);
1877                                 if ( !mru->mru_names ) {
1878                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1879                                                 *code = LDAP_SCHERR_BADNAME;
1880                                         *errp = ss;
1881                                         ldap_matchingruleuse_free(mru);
1882                                         return NULL;
1883                                 }
1884                         } else if ( !strcasecmp(sval,"DESC") ) {
1885                                 LDAP_FREE(sval);
1886                                 if ( seen_desc ) {
1887                                         *code = LDAP_SCHERR_DUPOPT;
1888                                         *errp = ss;
1889                                         ldap_matchingruleuse_free(mru);
1890                                         return(NULL);
1891                                 }
1892                                 seen_desc = 1;
1893                                 parse_whsp(&ss);
1894                                 kind = get_token(&ss,&sval);
1895                                 if ( kind != TK_QDSTRING ) {
1896                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1897                                         *errp = ss;
1898                                         LDAP_FREE(sval);
1899                                         ldap_matchingruleuse_free(mru);
1900                                         return NULL;
1901                                 }
1902                                 mru->mru_desc = sval;
1903                                 parse_whsp(&ss);
1904                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
1905                                 LDAP_FREE(sval);
1906                                 if ( seen_obsolete ) {
1907                                         *code = LDAP_SCHERR_DUPOPT;
1908                                         *errp = ss;
1909                                         ldap_matchingruleuse_free(mru);
1910                                         return(NULL);
1911                                 }
1912                                 seen_obsolete = 1;
1913                                 mru->mru_obsolete = LDAP_SCHEMA_YES;
1914                                 parse_whsp(&ss);
1915                         } else if ( !strcasecmp(sval,"APPLIES") ) {
1916                                 LDAP_FREE(sval);
1917                                 if ( seen_applies ) {
1918                                         *code = LDAP_SCHERR_DUPOPT;
1919                                         *errp = ss;
1920                                         ldap_matchingruleuse_free(mru);
1921                                         return(NULL);
1922                                 }
1923                                 seen_applies = 1;
1924                                 mru->mru_applies_oids = parse_oids(&ss,
1925                                                              code,
1926                                                              flags);
1927                                 if ( !mru->mru_applies_oids ) {
1928                                         *errp = ss;
1929                                         ldap_matchingruleuse_free(mru);
1930                                         return NULL;
1931                                 }
1932                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1933                                 /* Should be parse_qdstrings */
1934                                 ext_vals = parse_qdescrs(&ss, code);
1935                                 if ( !ext_vals ) {
1936                                         *errp = ss;
1937                                         ldap_matchingruleuse_free(mru);
1938                                         return NULL;
1939                                 }
1940                                 if ( add_extension(&mru->mru_extensions,
1941                                                     sval, ext_vals) ) {
1942                                         *code = LDAP_SCHERR_OUTOFMEM;
1943                                         *errp = ss;
1944                                         LDAP_FREE(sval);
1945                                         ldap_matchingruleuse_free(mru);
1946                                         return NULL;
1947                                 }
1948                         } else {
1949                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1950                                 *errp = ss;
1951                                 LDAP_FREE(sval);
1952                                 ldap_matchingruleuse_free(mru);
1953                                 return NULL;
1954                         }
1955                         break;
1956                 default:
1957                         *code = LDAP_SCHERR_UNEXPTOKEN;
1958                         *errp = ss;
1959                         LDAP_FREE(sval);
1960                         ldap_matchingruleuse_free(mru);
1961                         return NULL;
1962                 }
1963         }
1964 }
1965
1966 void
1967 ldap_attributetype_free(LDAPAttributeType * at)
1968 {
1969         LDAP_FREE(at->at_oid);
1970         if (at->at_names) LDAP_VFREE(at->at_names);
1971         if (at->at_desc) LDAP_FREE(at->at_desc);
1972         if (at->at_sup_oid) LDAP_FREE(at->at_sup_oid);
1973         if (at->at_equality_oid) LDAP_FREE(at->at_equality_oid);
1974         if (at->at_ordering_oid) LDAP_FREE(at->at_ordering_oid);
1975         if (at->at_substr_oid) LDAP_FREE(at->at_substr_oid);
1976         if (at->at_syntax_oid) LDAP_FREE(at->at_syntax_oid);
1977         free_extensions(at->at_extensions);
1978         LDAP_FREE(at);
1979 }
1980
1981 LDAPAttributeType *
1982 ldap_str2attributetype( LDAP_CONST char * s,
1983         int * code,
1984         LDAP_CONST char ** errp,
1985         LDAP_CONST unsigned flags )
1986 {
1987         int kind;
1988         const char * ss = s;
1989         char * sval;
1990         int seen_name = 0;
1991         int seen_desc = 0;
1992         int seen_obsolete = 0;
1993         int seen_sup = 0;
1994         int seen_equality = 0;
1995         int seen_ordering = 0;
1996         int seen_substr = 0;
1997         int seen_syntax = 0;
1998         int seen_usage = 0;
1999         LDAPAttributeType * at;
2000         char ** ext_vals;
2001         const char * savepos;
2002
2003         if ( !s ) {
2004                 *code = LDAP_SCHERR_EMPTY;
2005                 *errp = "";
2006                 return NULL;
2007         }
2008
2009         *errp = s;
2010         at = LDAP_CALLOC(1,sizeof(LDAPAttributeType));
2011
2012         if ( !at ) {
2013                 *code = LDAP_SCHERR_OUTOFMEM;
2014                 return NULL;
2015         }
2016
2017         kind = get_token(&ss,&sval);
2018         if ( kind != TK_LEFTPAREN ) {
2019                 *code = LDAP_SCHERR_NOLEFTPAREN;
2020                 LDAP_FREE(sval);
2021                 ldap_attributetype_free(at);
2022                 return NULL;
2023         }
2024
2025         /*
2026          * Definitions MUST begin with an OID in the numericoid format.
2027          * However, this routine is used by clients to parse the response
2028          * from servers and very well known servers will provide an OID
2029          * in the wrong format or even no OID at all.  We do our best to
2030          * extract info from those servers.
2031          */
2032         parse_whsp(&ss);
2033         savepos = ss;
2034         at->at_oid = ldap_int_parse_numericoid(&ss,code,0);
2035         if ( !at->at_oid ) {
2036                 if ( ( flags & ( LDAP_SCHEMA_ALLOW_NO_OID
2037                                 | LDAP_SCHEMA_ALLOW_OID_MACRO ) )
2038                             && (ss == savepos) ) {
2039                         /* Backtracking */
2040                         ss = savepos;
2041                         kind = get_token(&ss,&sval);
2042                         if ( kind == TK_BAREWORD ) {
2043                                 if ( !strcasecmp(sval, "NAME") ||
2044                                      !strcasecmp(sval, "DESC") ||
2045                                      !strcasecmp(sval, "OBSOLETE") ||
2046                                      !strcasecmp(sval, "SUP") ||
2047                                      !strcasecmp(sval, "EQUALITY") ||
2048                                      !strcasecmp(sval, "ORDERING") ||
2049                                      !strcasecmp(sval, "SUBSTR") ||
2050                                      !strcasecmp(sval, "SYNTAX") ||
2051                                      !strcasecmp(sval, "SINGLE-VALUE") ||
2052                                      !strcasecmp(sval, "COLLECTIVE") ||
2053                                      !strcasecmp(sval, "NO-USER-MODIFICATION") ||
2054                                      !strcasecmp(sval, "USAGE") ||
2055                                      !strncasecmp(sval, "X-", 2) ) {
2056                                         /* Missing OID, backtrack */
2057                                         ss = savepos;
2058                                 } else if ( flags
2059                                         & LDAP_SCHEMA_ALLOW_OID_MACRO) {
2060                                         /* Non-numerical OID ... */
2061                                         int len = ss-savepos;
2062                                         at->at_oid = LDAP_MALLOC(len+1);
2063                                         strncpy(at->at_oid, savepos, len);
2064                                         at->at_oid[len] = 0;
2065                                 }
2066                         }
2067                         LDAP_FREE(sval);
2068                 } else {
2069                         *errp = ss;
2070                         ldap_attributetype_free(at);
2071                         return NULL;
2072                 }
2073         }
2074         parse_whsp(&ss);
2075
2076         /*
2077          * Beyond this point we will be liberal and accept the items
2078          * in any order.
2079          */
2080         while (1) {
2081                 kind = get_token(&ss,&sval);
2082                 switch (kind) {
2083                 case TK_EOS:
2084                         *code = LDAP_SCHERR_NORIGHTPAREN;
2085                         *errp = ss;
2086                         ldap_attributetype_free(at);
2087                         return NULL;
2088                 case TK_RIGHTPAREN:
2089                         return at;
2090                 case TK_BAREWORD:
2091                         if ( !strcasecmp(sval,"NAME") ) {
2092                                 LDAP_FREE(sval);
2093                                 if ( seen_name ) {
2094                                         *code = LDAP_SCHERR_DUPOPT;
2095                                         *errp = ss;
2096                                         ldap_attributetype_free(at);
2097                                         return(NULL);
2098                                 }
2099                                 seen_name = 1;
2100                                 at->at_names = parse_qdescrs(&ss,code);
2101                                 if ( !at->at_names ) {
2102                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
2103                                                 *code = LDAP_SCHERR_BADNAME;
2104                                         *errp = ss;
2105                                         ldap_attributetype_free(at);
2106                                         return NULL;
2107                                 }
2108                         } else if ( !strcasecmp(sval,"DESC") ) {
2109                                 LDAP_FREE(sval);
2110                                 if ( seen_desc ) {
2111                                         *code = LDAP_SCHERR_DUPOPT;
2112                                         *errp = ss;
2113                                         ldap_attributetype_free(at);
2114                                         return(NULL);
2115                                 }
2116                                 seen_desc = 1;
2117                                 parse_whsp(&ss);
2118                                 kind = get_token(&ss,&sval);
2119                                 if ( kind != TK_QDSTRING ) {
2120                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2121                                         *errp = ss;
2122                                         LDAP_FREE(sval);
2123                                         ldap_attributetype_free(at);
2124                                         return NULL;
2125                                 }
2126                                 at->at_desc = sval;
2127                                 parse_whsp(&ss);
2128                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
2129                                 LDAP_FREE(sval);
2130                                 if ( seen_obsolete ) {
2131                                         *code = LDAP_SCHERR_DUPOPT;
2132                                         *errp = ss;
2133                                         ldap_attributetype_free(at);
2134                                         return(NULL);
2135                                 }
2136                                 seen_obsolete = 1;
2137                                 at->at_obsolete = LDAP_SCHEMA_YES;
2138                                 parse_whsp(&ss);
2139                         } else if ( !strcasecmp(sval,"SUP") ) {
2140                                 LDAP_FREE(sval);
2141                                 if ( seen_sup ) {
2142                                         *code = LDAP_SCHERR_DUPOPT;
2143                                         *errp = ss;
2144                                         ldap_attributetype_free(at);
2145                                         return(NULL);
2146                                 }
2147                                 seen_sup = 1;
2148                                 at->at_sup_oid = parse_woid(&ss,code);
2149                                 if ( !at->at_sup_oid ) {
2150                                         *errp = ss;
2151                                         ldap_attributetype_free(at);
2152                                         return NULL;
2153                                 }
2154                         } else if ( !strcasecmp(sval,"EQUALITY") ) {
2155                                 LDAP_FREE(sval);
2156                                 if ( seen_equality ) {
2157                                         *code = LDAP_SCHERR_DUPOPT;
2158                                         *errp = ss;
2159                                         ldap_attributetype_free(at);
2160                                         return(NULL);
2161                                 }
2162                                 seen_equality = 1;
2163                                 at->at_equality_oid = parse_woid(&ss,code);
2164                                 if ( !at->at_equality_oid ) {
2165                                         *errp = ss;
2166                                         ldap_attributetype_free(at);
2167                                         return NULL;
2168                                 }
2169                         } else if ( !strcasecmp(sval,"ORDERING") ) {
2170                                 LDAP_FREE(sval);
2171                                 if ( seen_ordering ) {
2172                                         *code = LDAP_SCHERR_DUPOPT;
2173                                         *errp = ss;
2174                                         ldap_attributetype_free(at);
2175                                         return(NULL);
2176                                 }
2177                                 seen_ordering = 1;
2178                                 at->at_ordering_oid = parse_woid(&ss,code);
2179                                 if ( !at->at_ordering_oid ) {
2180                                         *errp = ss;
2181                                         ldap_attributetype_free(at);
2182                                         return NULL;
2183                                 }
2184                         } else if ( !strcasecmp(sval,"SUBSTR") ) {
2185                                 LDAP_FREE(sval);
2186                                 if ( seen_substr ) {
2187                                         *code = LDAP_SCHERR_DUPOPT;
2188                                         *errp = ss;
2189                                         ldap_attributetype_free(at);
2190                                         return(NULL);
2191                                 }
2192                                 seen_substr = 1;
2193                                 at->at_substr_oid = parse_woid(&ss,code);
2194                                 if ( !at->at_substr_oid ) {
2195                                         *errp = ss;
2196                                         ldap_attributetype_free(at);
2197                                         return NULL;
2198                                 }
2199                         } else if ( !strcasecmp(sval,"SYNTAX") ) {
2200                                 LDAP_FREE(sval);
2201                                 if ( seen_syntax ) {
2202                                         *code = LDAP_SCHERR_DUPOPT;
2203                                         *errp = ss;
2204                                         ldap_attributetype_free(at);
2205                                         return(NULL);
2206                                 }
2207                                 seen_syntax = 1;
2208                                 parse_whsp(&ss);
2209                                 savepos = ss;
2210                                 at->at_syntax_oid =
2211                                         parse_noidlen(&ss,
2212                                                       code,
2213                                                       &at->at_syntax_len,
2214                                                       flags);
2215                                 if ( !at->at_syntax_oid ) {
2216                                     if ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO ) {
2217                                         kind = get_token(&ss,&sval);
2218                                         if (kind == TK_BAREWORD)
2219                                         {
2220                                             char *sp = strchr(sval, '{');
2221                                             at->at_syntax_oid = sval;
2222                                             if (sp)
2223                                             {
2224                                                 *sp++ = 0;
2225                                                 at->at_syntax_len = atoi(sp);
2226                                                 while ( LDAP_DIGIT(*sp) )
2227                                                         sp++;
2228                                                 if ( *sp != '}' ) {
2229                                                     *code = LDAP_SCHERR_UNEXPTOKEN;
2230                                                     *errp = ss;
2231                                                     ldap_attributetype_free(at);
2232                                                     return NULL;
2233                                                 }
2234                                             }
2235                                         }
2236                                     } else {
2237                                         *errp = ss;
2238                                         ldap_attributetype_free(at);
2239                                         return NULL;
2240                                     }
2241                                 }
2242                                 parse_whsp(&ss);
2243                         } else if ( !strcasecmp(sval,"SINGLE-VALUE") ) {
2244                                 LDAP_FREE(sval);
2245                                 if ( at->at_single_value ) {
2246                                         *code = LDAP_SCHERR_DUPOPT;
2247                                         *errp = ss;
2248                                         ldap_attributetype_free(at);
2249                                         return(NULL);
2250                                 }
2251                                 at->at_single_value = LDAP_SCHEMA_YES;
2252                                 parse_whsp(&ss);
2253                         } else if ( !strcasecmp(sval,"COLLECTIVE") ) {
2254                                 LDAP_FREE(sval);
2255                                 if ( at->at_collective ) {
2256                                         *code = LDAP_SCHERR_DUPOPT;
2257                                         *errp = ss;
2258                                         ldap_attributetype_free(at);
2259                                         return(NULL);
2260                                 }
2261                                 at->at_collective = LDAP_SCHEMA_YES;
2262                                 parse_whsp(&ss);
2263                         } else if ( !strcasecmp(sval,"NO-USER-MODIFICATION") ) {
2264                                 LDAP_FREE(sval);
2265                                 if ( at->at_no_user_mod ) {
2266                                         *code = LDAP_SCHERR_DUPOPT;
2267                                         *errp = ss;
2268                                         ldap_attributetype_free(at);
2269                                         return(NULL);
2270                                 }
2271                                 at->at_no_user_mod = LDAP_SCHEMA_YES;
2272                                 parse_whsp(&ss);
2273                         } else if ( !strcasecmp(sval,"USAGE") ) {
2274                                 LDAP_FREE(sval);
2275                                 if ( seen_usage ) {
2276                                         *code = LDAP_SCHERR_DUPOPT;
2277                                         *errp = ss;
2278                                         ldap_attributetype_free(at);
2279                                         return(NULL);
2280                                 }
2281                                 seen_usage = 1;
2282                                 parse_whsp(&ss);
2283                                 kind = get_token(&ss,&sval);
2284                                 if ( kind != TK_BAREWORD ) {
2285                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2286                                         *errp = ss;
2287                                         LDAP_FREE(sval);
2288                                         ldap_attributetype_free(at);
2289                                         return NULL;
2290                                 }
2291                                 if ( !strcasecmp(sval,"userApplications") )
2292                                         at->at_usage =
2293                                             LDAP_SCHEMA_USER_APPLICATIONS;
2294                                 else if ( !strcasecmp(sval,"directoryOperation") )
2295                                         at->at_usage =
2296                                             LDAP_SCHEMA_DIRECTORY_OPERATION;
2297                                 else if ( !strcasecmp(sval,"distributedOperation") )
2298                                         at->at_usage =
2299                                             LDAP_SCHEMA_DISTRIBUTED_OPERATION;
2300                                 else if ( !strcasecmp(sval,"dSAOperation") )
2301                                         at->at_usage =
2302                                             LDAP_SCHEMA_DSA_OPERATION;
2303                                 else {
2304                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2305                                         *errp = ss;
2306                                         LDAP_FREE(sval);
2307                                         ldap_attributetype_free(at);
2308                                         return NULL;
2309                                 }
2310                                 LDAP_FREE(sval);
2311                                 parse_whsp(&ss);
2312                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
2313                                 /* Should be parse_qdstrings */
2314                                 ext_vals = parse_qdescrs(&ss, code);
2315                                 if ( !ext_vals ) {
2316                                         *errp = ss;
2317                                         ldap_attributetype_free(at);
2318                                         return NULL;
2319                                 }
2320                                 if ( add_extension(&at->at_extensions,
2321                                                     sval, ext_vals) ) {
2322                                         *code = LDAP_SCHERR_OUTOFMEM;
2323                                         *errp = ss;
2324                                         LDAP_FREE(sval);
2325                                         ldap_attributetype_free(at);
2326                                         return NULL;
2327                                 }
2328                         } else {
2329                                 *code = LDAP_SCHERR_UNEXPTOKEN;
2330                                 *errp = ss;
2331                                 LDAP_FREE(sval);
2332                                 ldap_attributetype_free(at);
2333                                 return NULL;
2334                         }
2335                         break;
2336                 default:
2337                         *code = LDAP_SCHERR_UNEXPTOKEN;
2338                         *errp = ss;
2339                         LDAP_FREE(sval);
2340                         ldap_attributetype_free(at);
2341                         return NULL;
2342                 }
2343         }
2344 }
2345
2346 void
2347 ldap_objectclass_free(LDAPObjectClass * oc)
2348 {
2349         LDAP_FREE(oc->oc_oid);
2350         if (oc->oc_names) LDAP_VFREE(oc->oc_names);
2351         if (oc->oc_desc) LDAP_FREE(oc->oc_desc);
2352         if (oc->oc_sup_oids) LDAP_VFREE(oc->oc_sup_oids);
2353         if (oc->oc_at_oids_must) LDAP_VFREE(oc->oc_at_oids_must);
2354         if (oc->oc_at_oids_may) LDAP_VFREE(oc->oc_at_oids_may);
2355         free_extensions(oc->oc_extensions);
2356         LDAP_FREE(oc);
2357 }
2358
2359 LDAPObjectClass *
2360 ldap_str2objectclass( LDAP_CONST char * s,
2361         int * code,
2362         LDAP_CONST char ** errp,
2363         LDAP_CONST unsigned flags )
2364 {
2365         int kind;
2366         const char * ss = s;
2367         char * sval;
2368         int seen_name = 0;
2369         int seen_desc = 0;
2370         int seen_obsolete = 0;
2371         int seen_sup = 0;
2372         int seen_kind = 0;
2373         int seen_must = 0;
2374         int seen_may = 0;
2375         LDAPObjectClass * oc;
2376         char ** ext_vals;
2377         const char * savepos;
2378
2379         if ( !s ) {
2380                 *code = LDAP_SCHERR_EMPTY;
2381                 *errp = "";
2382                 return NULL;
2383         }
2384
2385         *errp = s;
2386         oc = LDAP_CALLOC(1,sizeof(LDAPObjectClass));
2387
2388         if ( !oc ) {
2389                 *code = LDAP_SCHERR_OUTOFMEM;
2390                 return NULL;
2391         }
2392         oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
2393
2394         kind = get_token(&ss,&sval);
2395         if ( kind != TK_LEFTPAREN ) {
2396                 *code = LDAP_SCHERR_NOLEFTPAREN;
2397                 LDAP_FREE(sval);
2398                 ldap_objectclass_free(oc);
2399                 return NULL;
2400         }
2401
2402         /*
2403          * Definitions MUST begin with an OID in the numericoid format.
2404          * However, this routine is used by clients to parse the response
2405          * from servers and very well known servers will provide an OID
2406          * in the wrong format or even no OID at all.  We do our best to
2407          * extract info from those servers.
2408          */
2409         parse_whsp(&ss);
2410         savepos = ss;
2411         oc->oc_oid = ldap_int_parse_numericoid(&ss,code,0);
2412         if ( !oc->oc_oid ) {
2413                 if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) {
2414                         /* Backtracking */
2415                         ss = savepos;
2416                         kind = get_token(&ss,&sval);
2417                         if ( kind == TK_BAREWORD ) {
2418                                 if ( !strcasecmp(sval, "NAME") ||
2419                                      !strcasecmp(sval, "DESC") ||
2420                                      !strcasecmp(sval, "OBSOLETE") ||
2421                                      !strcasecmp(sval, "SUP") ||
2422                                      !strcasecmp(sval, "ABSTRACT") ||
2423                                      !strcasecmp(sval, "STRUCTURAL") ||
2424                                      !strcasecmp(sval, "AUXILIARY") ||
2425                                      !strcasecmp(sval, "MUST") ||
2426                                      !strcasecmp(sval, "MAY") ||
2427                                      !strncasecmp(sval, "X-", 2) ) {
2428                                         /* Missing OID, backtrack */
2429                                         ss = savepos;
2430                                 } else if ( flags &
2431                                         LDAP_SCHEMA_ALLOW_OID_MACRO ) {
2432                                         /* Non-numerical OID, ignore */
2433                                         int len = ss-savepos;
2434                                         oc->oc_oid = LDAP_MALLOC(len+1);
2435                                         strncpy(oc->oc_oid, savepos, len);
2436                                         oc->oc_oid[len] = 0;
2437                                 }
2438                         }
2439                         LDAP_FREE(sval);
2440                 } else {
2441                         *errp = ss;
2442                         ldap_objectclass_free(oc);
2443                         return NULL;
2444                 }
2445         }
2446         parse_whsp(&ss);
2447
2448         /*
2449          * Beyond this point we will be liberal an accept the items
2450          * in any order.
2451          */
2452         while (1) {
2453                 kind = get_token(&ss,&sval);
2454                 switch (kind) {
2455                 case TK_EOS:
2456                         *code = LDAP_SCHERR_NORIGHTPAREN;
2457                         *errp = ss;
2458                         ldap_objectclass_free(oc);
2459                         return NULL;
2460                 case TK_RIGHTPAREN:
2461                         return oc;
2462                 case TK_BAREWORD:
2463                         if ( !strcasecmp(sval,"NAME") ) {
2464                                 LDAP_FREE(sval);
2465                                 if ( seen_name ) {
2466                                         *code = LDAP_SCHERR_DUPOPT;
2467                                         *errp = ss;
2468                                         ldap_objectclass_free(oc);
2469                                         return(NULL);
2470                                 }
2471                                 seen_name = 1;
2472                                 oc->oc_names = parse_qdescrs(&ss,code);
2473                                 if ( !oc->oc_names ) {
2474                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
2475                                                 *code = LDAP_SCHERR_BADNAME;
2476                                         *errp = ss;
2477                                         ldap_objectclass_free(oc);
2478                                         return NULL;
2479                                 }
2480                         } else if ( !strcasecmp(sval,"DESC") ) {
2481                                 LDAP_FREE(sval);
2482                                 if ( seen_desc ) {
2483                                         *code = LDAP_SCHERR_DUPOPT;
2484                                         *errp = ss;
2485                                         ldap_objectclass_free(oc);
2486                                         return(NULL);
2487                                 }
2488                                 seen_desc = 1;
2489                                 parse_whsp(&ss);
2490                                 kind = get_token(&ss,&sval);
2491                                 if ( kind != TK_QDSTRING ) {
2492                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2493                                         *errp = ss;
2494                                         LDAP_FREE(sval);
2495                                         ldap_objectclass_free(oc);
2496                                         return NULL;
2497                                 }
2498                                 oc->oc_desc = sval;
2499                                 parse_whsp(&ss);
2500                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
2501                                 LDAP_FREE(sval);
2502                                 if ( seen_obsolete ) {
2503                                         *code = LDAP_SCHERR_DUPOPT;
2504                                         *errp = ss;
2505                                         ldap_objectclass_free(oc);
2506                                         return(NULL);
2507                                 }
2508                                 seen_obsolete = 1;
2509                                 oc->oc_obsolete = LDAP_SCHEMA_YES;
2510                                 parse_whsp(&ss);
2511                         } else if ( !strcasecmp(sval,"SUP") ) {
2512                                 LDAP_FREE(sval);
2513                                 if ( seen_sup ) {
2514                                         *code = LDAP_SCHERR_DUPOPT;
2515                                         *errp = ss;
2516                                         ldap_objectclass_free(oc);
2517                                         return(NULL);
2518                                 }
2519                                 seen_sup = 1;
2520                                 oc->oc_sup_oids = parse_oids(&ss,
2521                                                              code,
2522                                                              flags);
2523                                 if ( !oc->oc_sup_oids ) {
2524                                         *errp = ss;
2525                                         ldap_objectclass_free(oc);
2526                                         return NULL;
2527                                 }
2528                         } else if ( !strcasecmp(sval,"ABSTRACT") ) {
2529                                 LDAP_FREE(sval);
2530                                 if ( seen_kind ) {
2531                                         *code = LDAP_SCHERR_DUPOPT;
2532                                         *errp = ss;
2533                                         ldap_objectclass_free(oc);
2534                                         return(NULL);
2535                                 }
2536                                 seen_kind = 1;
2537                                 oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
2538                                 parse_whsp(&ss);
2539                         } else if ( !strcasecmp(sval,"STRUCTURAL") ) {
2540                                 LDAP_FREE(sval);
2541                                 if ( seen_kind ) {
2542                                         *code = LDAP_SCHERR_DUPOPT;
2543                                         *errp = ss;
2544                                         ldap_objectclass_free(oc);
2545                                         return(NULL);
2546                                 }
2547                                 seen_kind = 1;
2548                                 oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
2549                                 parse_whsp(&ss);
2550                         } else if ( !strcasecmp(sval,"AUXILIARY") ) {
2551                                 LDAP_FREE(sval);
2552                                 if ( seen_kind ) {
2553                                         *code = LDAP_SCHERR_DUPOPT;
2554                                         *errp = ss;
2555                                         ldap_objectclass_free(oc);
2556                                         return(NULL);
2557                                 }
2558                                 seen_kind = 1;
2559                                 oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
2560                                 parse_whsp(&ss);
2561                         } else if ( !strcasecmp(sval,"MUST") ) {
2562                                 LDAP_FREE(sval);
2563                                 if ( seen_must ) {
2564                                         *code = LDAP_SCHERR_DUPOPT;
2565                                         *errp = ss;
2566                                         ldap_objectclass_free(oc);
2567                                         return(NULL);
2568                                 }
2569                                 seen_must = 1;
2570                                 oc->oc_at_oids_must = parse_oids(&ss,code,0);
2571                                 if ( !oc->oc_at_oids_must ) {
2572                                         *errp = ss;
2573                                         ldap_objectclass_free(oc);
2574                                         return NULL;
2575                                 }
2576                                 parse_whsp(&ss);
2577                         } else if ( !strcasecmp(sval,"MAY") ) {
2578                                 LDAP_FREE(sval);
2579                                 if ( seen_may ) {
2580                                         *code = LDAP_SCHERR_DUPOPT;
2581                                         *errp = ss;
2582                                         ldap_objectclass_free(oc);
2583                                         return(NULL);
2584                                 }
2585                                 seen_may = 1;
2586                                 oc->oc_at_oids_may = parse_oids(&ss,code,0);
2587                                 if ( !oc->oc_at_oids_may ) {
2588                                         *errp = ss;
2589                                         ldap_objectclass_free(oc);
2590                                         return NULL;
2591                                 }
2592                                 parse_whsp(&ss);
2593                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
2594                                 /* Should be parse_qdstrings */
2595                                 ext_vals = parse_qdescrs(&ss, code);
2596                                 if ( !ext_vals ) {
2597                                         *errp = ss;
2598                                         ldap_objectclass_free(oc);
2599                                         return NULL;
2600                                 }
2601                                 if ( add_extension(&oc->oc_extensions,
2602                                                     sval, ext_vals) ) {
2603                                         *code = LDAP_SCHERR_OUTOFMEM;
2604                                         *errp = ss;
2605                                         LDAP_FREE(sval);
2606                                         ldap_objectclass_free(oc);
2607                                         return NULL;
2608                                 }
2609                         } else {
2610                                 *code = LDAP_SCHERR_UNEXPTOKEN;
2611                                 *errp = ss;
2612                                 LDAP_FREE(sval);
2613                                 ldap_objectclass_free(oc);
2614                                 return NULL;
2615                         }
2616                         break;
2617                 default:
2618                         *code = LDAP_SCHERR_UNEXPTOKEN;
2619                         *errp = ss;
2620                         LDAP_FREE(sval);
2621                         ldap_objectclass_free(oc);
2622                         return NULL;
2623                 }
2624         }
2625 }
2626
2627 void
2628 ldap_contentrule_free(LDAPContentRule * cr)
2629 {
2630         LDAP_FREE(cr->cr_oid);
2631         if (cr->cr_names) LDAP_VFREE(cr->cr_names);
2632         if (cr->cr_desc) LDAP_FREE(cr->cr_desc);
2633         if (cr->cr_oc_oids_aux) LDAP_VFREE(cr->cr_oc_oids_aux);
2634         if (cr->cr_at_oids_must) LDAP_VFREE(cr->cr_at_oids_must);
2635         if (cr->cr_at_oids_may) LDAP_VFREE(cr->cr_at_oids_may);
2636         if (cr->cr_at_oids_not) LDAP_VFREE(cr->cr_at_oids_not);
2637         free_extensions(cr->cr_extensions);
2638         LDAP_FREE(cr);
2639 }
2640
2641 LDAPContentRule *
2642 ldap_str2contentrule( LDAP_CONST char * s,
2643         int * code,
2644         LDAP_CONST char ** errp,
2645         LDAP_CONST unsigned flags )
2646 {
2647         int kind;
2648         const char * ss = s;
2649         char * sval;
2650         int seen_name = 0;
2651         int seen_desc = 0;
2652         int seen_obsolete = 0;
2653         int seen_aux = 0;
2654         int seen_must = 0;
2655         int seen_may = 0;
2656         int seen_not = 0;
2657         LDAPContentRule * cr;
2658         char ** ext_vals;
2659         const char * savepos;
2660
2661         if ( !s ) {
2662                 *code = LDAP_SCHERR_EMPTY;
2663                 *errp = "";
2664                 return NULL;
2665         }
2666
2667         *errp = s;
2668         cr = LDAP_CALLOC(1,sizeof(LDAPContentRule));
2669
2670         if ( !cr ) {
2671                 *code = LDAP_SCHERR_OUTOFMEM;
2672                 return NULL;
2673         }
2674
2675         kind = get_token(&ss,&sval);
2676         if ( kind != TK_LEFTPAREN ) {
2677                 *code = LDAP_SCHERR_NOLEFTPAREN;
2678                 LDAP_FREE(sval);
2679                 ldap_contentrule_free(cr);
2680                 return NULL;
2681         }
2682
2683         /*
2684          * Definitions MUST begin with an OID in the numericoid format.
2685          */
2686         parse_whsp(&ss);
2687         savepos = ss;
2688         cr->cr_oid = ldap_int_parse_numericoid(&ss,code,0);
2689         if ( !cr->cr_oid ) {
2690                 if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) {
2691                         /* Backtracking */
2692                         ss = savepos;
2693                         kind = get_token(&ss,&sval);
2694                         if ( kind == TK_BAREWORD ) {
2695                                 if ( !strcasecmp(sval, "NAME") ||
2696                                      !strcasecmp(sval, "DESC") ||
2697                                      !strcasecmp(sval, "OBSOLETE") ||
2698                                      !strcasecmp(sval, "AUX") ||
2699                                      !strcasecmp(sval, "MUST") ||
2700                                      !strcasecmp(sval, "MAY") ||
2701                                      !strcasecmp(sval, "NOT") ||
2702                                      !strncasecmp(sval, "X-", 2) ) {
2703                                         /* Missing OID, backtrack */
2704                                         ss = savepos;
2705                                 } else if ( flags &
2706                                         LDAP_SCHEMA_ALLOW_OID_MACRO ) {
2707                                         /* Non-numerical OID, ignore */
2708                                         int len = ss-savepos;
2709                                         cr->cr_oid = LDAP_MALLOC(len+1);
2710                                         strncpy(cr->cr_oid, savepos, len);
2711                                         cr->cr_oid[len] = 0;
2712                                 }
2713                         }
2714                         LDAP_FREE(sval);
2715                 } else {
2716                         *errp = ss;
2717                         ldap_contentrule_free(cr);
2718                         return NULL;
2719                 }
2720         }
2721         parse_whsp(&ss);
2722
2723         /*
2724          * Beyond this point we will be liberal an accept the items
2725          * in any order.
2726          */
2727         while (1) {
2728                 kind = get_token(&ss,&sval);
2729                 switch (kind) {
2730                 case TK_EOS:
2731                         *code = LDAP_SCHERR_NORIGHTPAREN;
2732                         *errp = ss;
2733                         ldap_contentrule_free(cr);
2734                         return NULL;
2735                 case TK_RIGHTPAREN:
2736                         return cr;
2737                 case TK_BAREWORD:
2738                         if ( !strcasecmp(sval,"NAME") ) {
2739                                 LDAP_FREE(sval);
2740                                 if ( seen_name ) {
2741                                         *code = LDAP_SCHERR_DUPOPT;
2742                                         *errp = ss;
2743                                         ldap_contentrule_free(cr);
2744                                         return(NULL);
2745                                 }
2746                                 seen_name = 1;
2747                                 cr->cr_names = parse_qdescrs(&ss,code);
2748                                 if ( !cr->cr_names ) {
2749                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
2750                                                 *code = LDAP_SCHERR_BADNAME;
2751                                         *errp = ss;
2752                                         ldap_contentrule_free(cr);
2753                                         return NULL;
2754                                 }
2755                         } else if ( !strcasecmp(sval,"DESC") ) {
2756                                 LDAP_FREE(sval);
2757                                 if ( seen_desc ) {
2758                                         *code = LDAP_SCHERR_DUPOPT;
2759                                         *errp = ss;
2760                                         ldap_contentrule_free(cr);
2761                                         return(NULL);
2762                                 }
2763                                 seen_desc = 1;
2764                                 parse_whsp(&ss);
2765                                 kind = get_token(&ss,&sval);
2766                                 if ( kind != TK_QDSTRING ) {
2767                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2768                                         *errp = ss;
2769                                         LDAP_FREE(sval);
2770                                         ldap_contentrule_free(cr);
2771                                         return NULL;
2772                                 }
2773                                 cr->cr_desc = sval;
2774                                 parse_whsp(&ss);
2775                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
2776                                 LDAP_FREE(sval);
2777                                 if ( seen_obsolete ) {
2778                                         *code = LDAP_SCHERR_DUPOPT;
2779                                         *errp = ss;
2780                                         ldap_contentrule_free(cr);
2781                                         return(NULL);
2782                                 }
2783                                 seen_obsolete = 1;
2784                                 cr->cr_obsolete = LDAP_SCHEMA_YES;
2785                                 parse_whsp(&ss);
2786                         } else if ( !strcasecmp(sval,"AUX") ) {
2787                                 LDAP_FREE(sval);
2788                                 if ( seen_aux ) {
2789                                         *code = LDAP_SCHERR_DUPOPT;
2790                                         *errp = ss;
2791                                         ldap_contentrule_free(cr);
2792                                         return(NULL);
2793                                 }
2794                                 seen_aux = 1;
2795                                 cr->cr_oc_oids_aux = parse_oids(&ss,code,0);
2796                                 if ( !cr->cr_oc_oids_aux ) {
2797                                         *errp = ss;
2798                                         ldap_contentrule_free(cr);
2799                                         return NULL;
2800                                 }
2801                                 parse_whsp(&ss);
2802                         } else if ( !strcasecmp(sval,"MUST") ) {
2803                                 LDAP_FREE(sval);
2804                                 if ( seen_must ) {
2805                                         *code = LDAP_SCHERR_DUPOPT;
2806                                         *errp = ss;
2807                                         ldap_contentrule_free(cr);
2808                                         return(NULL);
2809                                 }
2810                                 seen_must = 1;
2811                                 cr->cr_at_oids_must = parse_oids(&ss,code,0);
2812                                 if ( !cr->cr_at_oids_must ) {
2813                                         *errp = ss;
2814                                         ldap_contentrule_free(cr);
2815                                         return NULL;
2816                                 }
2817                                 parse_whsp(&ss);
2818                         } else if ( !strcasecmp(sval,"MAY") ) {
2819                                 LDAP_FREE(sval);
2820                                 if ( seen_may ) {
2821                                         *code = LDAP_SCHERR_DUPOPT;
2822                                         *errp = ss;
2823                                         ldap_contentrule_free(cr);
2824                                         return(NULL);
2825                                 }
2826                                 seen_may = 1;
2827                                 cr->cr_at_oids_may = parse_oids(&ss,code,0);
2828                                 if ( !cr->cr_at_oids_may ) {
2829                                         *errp = ss;
2830                                         ldap_contentrule_free(cr);
2831                                         return NULL;
2832                                 }
2833                                 parse_whsp(&ss);
2834                         } else if ( !strcasecmp(sval,"NOT") ) {
2835                                 LDAP_FREE(sval);
2836                                 if ( seen_not ) {
2837                                         *code = LDAP_SCHERR_DUPOPT;
2838                                         *errp = ss;
2839                                         ldap_contentrule_free(cr);
2840                                         return(NULL);
2841                                 }
2842                                 seen_not = 1;
2843                                 cr->cr_at_oids_not = parse_oids(&ss,code,0);
2844                                 if ( !cr->cr_at_oids_not ) {
2845                                         *errp = ss;
2846                                         ldap_contentrule_free(cr);
2847                                         return NULL;
2848                                 }
2849                                 parse_whsp(&ss);
2850                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
2851                                 /* Should be parse_qdstrings */
2852                                 ext_vals = parse_qdescrs(&ss, code);
2853                                 if ( !ext_vals ) {
2854                                         *errp = ss;
2855                                         ldap_contentrule_free(cr);
2856                                         return NULL;
2857                                 }
2858                                 if ( add_extension(&cr->cr_extensions,
2859                                                     sval, ext_vals) ) {
2860                                         *code = LDAP_SCHERR_OUTOFMEM;
2861                                         *errp = ss;
2862                                         LDAP_FREE(sval);
2863                                         ldap_contentrule_free(cr);
2864                                         return NULL;
2865                                 }
2866                         } else {
2867                                 *code = LDAP_SCHERR_UNEXPTOKEN;
2868                                 *errp = ss;
2869                                 LDAP_FREE(sval);
2870                                 ldap_contentrule_free(cr);
2871                                 return NULL;
2872                         }
2873                         break;
2874                 default:
2875                         *code = LDAP_SCHERR_UNEXPTOKEN;
2876                         *errp = ss;
2877                         LDAP_FREE(sval);
2878                         ldap_contentrule_free(cr);
2879                         return NULL;
2880                 }
2881         }
2882 }
2883
2884 void
2885 ldap_structurerule_free(LDAPStructureRule * sr)
2886 {
2887         if (sr->sr_names) LDAP_VFREE(sr->sr_names);
2888         if (sr->sr_desc) LDAP_FREE(sr->sr_desc);
2889         if (sr->sr_nameform) LDAP_FREE(sr->sr_nameform);
2890         if (sr->sr_sup_ruleids) LDAP_FREE(sr->sr_sup_ruleids);
2891         free_extensions(sr->sr_extensions);
2892         LDAP_FREE(sr);
2893 }
2894
2895 LDAPStructureRule *
2896 ldap_str2structurerule( LDAP_CONST char * s,
2897         int * code,
2898         LDAP_CONST char ** errp,
2899         LDAP_CONST unsigned flags )
2900 {
2901         int kind, ret;
2902         const char * ss = s;
2903         char * sval;
2904         int seen_name = 0;
2905         int seen_desc = 0;
2906         int seen_obsolete = 0;
2907         int seen_nameform = 0;
2908         LDAPStructureRule * sr;
2909         char ** ext_vals;
2910         const char * savepos;
2911
2912         if ( !s ) {
2913                 *code = LDAP_SCHERR_EMPTY;
2914                 *errp = "";
2915                 return NULL;
2916         }
2917
2918         *errp = s;
2919         sr = LDAP_CALLOC(1,sizeof(LDAPStructureRule));
2920
2921         if ( !sr ) {
2922                 *code = LDAP_SCHERR_OUTOFMEM;
2923                 return NULL;
2924         }
2925
2926         kind = get_token(&ss,&sval);
2927         if ( kind != TK_LEFTPAREN ) {
2928                 *code = LDAP_SCHERR_NOLEFTPAREN;
2929                 LDAP_FREE(sval);
2930                 ldap_structurerule_free(sr);
2931                 return NULL;
2932         }
2933
2934         /*
2935          * Definitions MUST begin with a ruleid.
2936          */
2937         parse_whsp(&ss);
2938         savepos = ss;
2939         ret = ldap_int_parse_ruleid(&ss,code,0,&sr->sr_ruleid);
2940         if ( ret ) {
2941                 *errp = ss;
2942                 ldap_structurerule_free(sr);
2943                 return NULL;
2944         }
2945         parse_whsp(&ss);
2946
2947         /*
2948          * Beyond this point we will be liberal an accept the items
2949          * in any order.
2950          */
2951         while (1) {
2952                 kind = get_token(&ss,&sval);
2953                 switch (kind) {
2954                 case TK_EOS:
2955                         *code = LDAP_SCHERR_NORIGHTPAREN;
2956                         *errp = ss;
2957                         ldap_structurerule_free(sr);
2958                         return NULL;
2959                 case TK_RIGHTPAREN:
2960                         if( !seen_nameform ) {
2961                                 *code = LDAP_SCHERR_MISSING;
2962                                 ldap_structurerule_free(sr);
2963                                 return NULL;
2964                         }
2965                         return sr;
2966                 case TK_BAREWORD:
2967                         if ( !strcasecmp(sval,"NAME") ) {
2968                                 LDAP_FREE(sval);
2969                                 if ( seen_name ) {
2970                                         *code = LDAP_SCHERR_DUPOPT;
2971                                         *errp = ss;
2972                                         ldap_structurerule_free(sr);
2973                                         return(NULL);
2974                                 }
2975                                 seen_name = 1;
2976                                 sr->sr_names = parse_qdescrs(&ss,code);
2977                                 if ( !sr->sr_names ) {
2978                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
2979                                                 *code = LDAP_SCHERR_BADNAME;
2980                                         *errp = ss;
2981                                         ldap_structurerule_free(sr);
2982                                         return NULL;
2983                                 }
2984                         } else if ( !strcasecmp(sval,"DESC") ) {
2985                                 LDAP_FREE(sval);
2986                                 if ( seen_desc ) {
2987                                         *code = LDAP_SCHERR_DUPOPT;
2988                                         *errp = ss;
2989                                         ldap_structurerule_free(sr);
2990                                         return(NULL);
2991                                 }
2992                                 seen_desc = 1;
2993                                 parse_whsp(&ss);
2994                                 kind = get_token(&ss,&sval);
2995                                 if ( kind != TK_QDSTRING ) {
2996                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2997                                         *errp = ss;
2998                                         LDAP_FREE(sval);
2999                                         ldap_structurerule_free(sr);
3000                                         return NULL;
3001                                 }
3002                                 sr->sr_desc = sval;
3003                                 parse_whsp(&ss);
3004                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
3005                                 LDAP_FREE(sval);
3006                                 if ( seen_obsolete ) {
3007                                         *code = LDAP_SCHERR_DUPOPT;
3008                                         *errp = ss;
3009                                         ldap_structurerule_free(sr);
3010                                         return(NULL);
3011                                 }
3012                                 seen_obsolete = 1;
3013                                 sr->sr_obsolete = LDAP_SCHEMA_YES;
3014                                 parse_whsp(&ss);
3015                         } else if ( !strcasecmp(sval,"FORM") ) {
3016                                 LDAP_FREE(sval);
3017                                 if ( seen_nameform ) {
3018                                         *code = LDAP_SCHERR_DUPOPT;
3019                                         *errp = ss;
3020                                         ldap_structurerule_free(sr);
3021                                         return(NULL);
3022                                 }
3023                                 seen_nameform = 1;
3024                                 sr->sr_nameform = parse_woid(&ss,code);
3025                                 if ( !sr->sr_nameform ) {
3026                                         *errp = ss;
3027                                         ldap_structurerule_free(sr);
3028                                         return NULL;
3029                                 }
3030                                 parse_whsp(&ss);
3031                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
3032                                 /* Should be parse_qdstrings */
3033                                 ext_vals = parse_qdescrs(&ss, code);
3034                                 if ( !ext_vals ) {
3035                                         *errp = ss;
3036                                         ldap_structurerule_free(sr);
3037                                         return NULL;
3038                                 }
3039                                 if ( add_extension(&sr->sr_extensions,
3040                                                     sval, ext_vals) ) {
3041                                         *code = LDAP_SCHERR_OUTOFMEM;
3042                                         *errp = ss;
3043                                         LDAP_FREE(sval);
3044                                         ldap_structurerule_free(sr);
3045                                         return NULL;
3046                                 }
3047                         } else {
3048                                 *code = LDAP_SCHERR_UNEXPTOKEN;
3049                                 *errp = ss;
3050                                 LDAP_FREE(sval);
3051                                 ldap_structurerule_free(sr);
3052                                 return NULL;
3053                         }
3054                         break;
3055                 default:
3056                         *code = LDAP_SCHERR_UNEXPTOKEN;
3057                         *errp = ss;
3058                         LDAP_FREE(sval);
3059                         ldap_structurerule_free(sr);
3060                         return NULL;
3061                 }
3062         }
3063 }
3064
3065 void
3066 ldap_nameform_free(LDAPNameForm * nf)
3067 {
3068         LDAP_FREE(nf->nf_oid);
3069         if (nf->nf_names) LDAP_VFREE(nf->nf_names);
3070         if (nf->nf_desc) LDAP_FREE(nf->nf_desc);
3071         if (nf->nf_objectclass) LDAP_FREE(nf->nf_objectclass);
3072         if (nf->nf_at_oids_must) LDAP_VFREE(nf->nf_at_oids_must);
3073         if (nf->nf_at_oids_may) LDAP_VFREE(nf->nf_at_oids_may);
3074         free_extensions(nf->nf_extensions);
3075         LDAP_FREE(nf);
3076 }
3077
3078 LDAPNameForm *
3079 ldap_str2nameform( LDAP_CONST char * s,
3080         int * code,
3081         LDAP_CONST char ** errp,
3082         LDAP_CONST unsigned flags )
3083 {
3084         int kind;
3085         const char * ss = s;
3086         char * sval;
3087         int seen_name = 0;
3088         int seen_desc = 0;
3089         int seen_obsolete = 0;
3090         int seen_class = 0;
3091         int seen_must = 0;
3092         int seen_may = 0;
3093         LDAPNameForm * nf;
3094         char ** ext_vals;
3095         const char * savepos;
3096
3097         if ( !s ) {
3098                 *code = LDAP_SCHERR_EMPTY;
3099                 *errp = "";
3100                 return NULL;
3101         }
3102
3103         *errp = s;
3104         nf = LDAP_CALLOC(1,sizeof(LDAPNameForm));
3105
3106         if ( !nf ) {
3107                 *code = LDAP_SCHERR_OUTOFMEM;
3108                 return NULL;
3109         }
3110
3111         kind = get_token(&ss,&sval);
3112         if ( kind != TK_LEFTPAREN ) {
3113                 *code = LDAP_SCHERR_NOLEFTPAREN;
3114                 LDAP_FREE(sval);
3115                 ldap_nameform_free(nf);
3116                 return NULL;
3117         }
3118
3119         /*
3120          * Definitions MUST begin with an OID in the numericoid format.
3121          * However, this routine is used by clients to parse the response
3122          * from servers and very well known servers will provide an OID
3123          * in the wrong format or even no OID at all.  We do our best to
3124          * extract info from those servers.
3125          */
3126         parse_whsp(&ss);
3127         savepos = ss;
3128         nf->nf_oid = ldap_int_parse_numericoid(&ss,code,0);
3129         if ( !nf->nf_oid ) {
3130                 *errp = ss;
3131                 ldap_nameform_free(nf);
3132                 return NULL;
3133         }
3134         parse_whsp(&ss);
3135
3136         /*
3137          * Beyond this point we will be liberal an accept the items
3138          * in any order.
3139          */
3140         while (1) {
3141                 kind = get_token(&ss,&sval);
3142                 switch (kind) {
3143                 case TK_EOS:
3144                         *code = LDAP_SCHERR_NORIGHTPAREN;
3145                         *errp = ss;
3146                         ldap_nameform_free(nf);
3147                         return NULL;
3148                 case TK_RIGHTPAREN:
3149                         if( !seen_class || !seen_must ) {
3150                                 *code = LDAP_SCHERR_MISSING;
3151                                 ldap_nameform_free(nf);
3152                                 return NULL;
3153                         }
3154                         return nf;
3155                 case TK_BAREWORD:
3156                         if ( !strcasecmp(sval,"NAME") ) {
3157                                 LDAP_FREE(sval);
3158                                 if ( seen_name ) {
3159                                         *code = LDAP_SCHERR_DUPOPT;
3160                                         *errp = ss;
3161                                         ldap_nameform_free(nf);
3162                                         return(NULL);
3163                                 }
3164                                 seen_name = 1;
3165                                 nf->nf_names = parse_qdescrs(&ss,code);
3166                                 if ( !nf->nf_names ) {
3167                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
3168                                                 *code = LDAP_SCHERR_BADNAME;
3169                                         *errp = ss;
3170                                         ldap_nameform_free(nf);
3171                                         return NULL;
3172                                 }
3173                         } else if ( !strcasecmp(sval,"DESC") ) {
3174                                 LDAP_FREE(sval);
3175                                 if ( seen_desc ) {
3176                                         *code = LDAP_SCHERR_DUPOPT;
3177                                         *errp = ss;
3178                                         ldap_nameform_free(nf);
3179                                         return(NULL);
3180                                 }
3181                                 seen_desc = 1;
3182                                 parse_whsp(&ss);
3183                                 kind = get_token(&ss,&sval);
3184                                 if ( kind != TK_QDSTRING ) {
3185                                         *code = LDAP_SCHERR_UNEXPTOKEN;
3186                                         *errp = ss;
3187                                         LDAP_FREE(sval);
3188                                         ldap_nameform_free(nf);
3189                                         return NULL;
3190                                 }
3191                                 nf->nf_desc = sval;
3192                                 parse_whsp(&ss);
3193                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
3194                                 LDAP_FREE(sval);
3195                                 if ( seen_obsolete ) {
3196                                         *code = LDAP_SCHERR_DUPOPT;
3197                                         *errp = ss;
3198                                         ldap_nameform_free(nf);
3199                                         return(NULL);
3200                                 }
3201                                 seen_obsolete = 1;
3202                                 nf->nf_obsolete = LDAP_SCHEMA_YES;
3203                                 parse_whsp(&ss);
3204                         } else if ( !strcasecmp(sval,"MUST") ) {
3205                                 LDAP_FREE(sval);
3206                                 if ( seen_must ) {
3207                                         *code = LDAP_SCHERR_DUPOPT;
3208                                         *errp = ss;
3209                                         ldap_nameform_free(nf);
3210                                         return(NULL);
3211                                 }
3212                                 seen_must = 1;
3213                                 nf->nf_at_oids_must = parse_oids(&ss,code,0);
3214                                 if ( !nf->nf_at_oids_must ) {
3215                                         *errp = ss;
3216                                         ldap_nameform_free(nf);
3217                                         return NULL;
3218                                 }
3219                                 parse_whsp(&ss);
3220                         } else if ( !strcasecmp(sval,"MAY") ) {
3221                                 LDAP_FREE(sval);
3222                                 if ( seen_may ) {
3223                                         *code = LDAP_SCHERR_DUPOPT;
3224                                         *errp = ss;
3225                                         ldap_nameform_free(nf);
3226                                         return(NULL);
3227                                 }
3228                                 seen_may = 1;
3229                                 nf->nf_at_oids_may = parse_oids(&ss,code,0);
3230                                 if ( !nf->nf_at_oids_may ) {
3231                                         *errp = ss;
3232                                         ldap_nameform_free(nf);
3233                                         return NULL;
3234                                 }
3235                                 parse_whsp(&ss);
3236                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
3237                                 /* Should be parse_qdstrings */
3238                                 ext_vals = parse_qdescrs(&ss, code);
3239                                 if ( !ext_vals ) {
3240                                         *errp = ss;
3241                                         ldap_nameform_free(nf);
3242                                         return NULL;
3243                                 }
3244                                 if ( add_extension(&nf->nf_extensions,
3245                                                     sval, ext_vals) ) {
3246                                         *code = LDAP_SCHERR_OUTOFMEM;
3247                                         *errp = ss;
3248                                         LDAP_FREE(sval);
3249                                         ldap_nameform_free(nf);
3250                                         return NULL;
3251                                 }
3252                         } else {
3253                                 *code = LDAP_SCHERR_UNEXPTOKEN;
3254                                 *errp = ss;
3255                                 LDAP_FREE(sval);
3256                                 ldap_nameform_free(nf);
3257                                 return NULL;
3258                         }
3259                         break;
3260                 default:
3261                         *code = LDAP_SCHERR_UNEXPTOKEN;
3262                         *errp = ss;
3263                         LDAP_FREE(sval);
3264                         ldap_nameform_free(nf);
3265                         return NULL;
3266                 }
3267         }
3268 }
3269
3270 static char *const err2text[] = {
3271         N_("Success"),
3272         N_("Out of memory"),
3273         N_("Unexpected token"),
3274         N_("Missing opening parenthesis"),
3275         N_("Missing closing parenthesis"),
3276         N_("Expecting digit"),
3277         N_("Expecting a name"),
3278         N_("Bad description"),
3279         N_("Bad superiors"),
3280         N_("Duplicate option"),
3281         N_("Unexpected end of data"),
3282         N_("Missing required field"),
3283         N_("Out of order field")
3284 };
3285
3286 char *
3287 ldap_scherr2str(int code)
3288 {
3289         if ( code < 0 || code >= (int)(sizeof(err2text)/sizeof(char *)) ) {
3290                 return _("Unknown error");
3291         } else {
3292                 return _(err2text[code]);
3293         }
3294 }