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