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