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