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