]> git.sur5r.net Git - openldap/blob - libraries/libldap/schema.c
Fix up NT port after thread changes
[openldap] / libraries / libldap / schema.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1999-2000 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/ctype.h>
17 #include <ac/string.h>
18 #include <ac/time.h>
19
20 #include "ldap-int.h"
21
22 #include <ldap_schema.h>
23
24 static LDAP_CONST char *
25 choose_name( char *names[], LDAP_CONST char *fallback )
26 {
27         return( (names != NULL && names[0] != NULL) ? names[0] : fallback );
28 }
29
30 LDAP_CONST char *
31 ldap_syntax2name( LDAP_SYNTAX * syn )
32 {
33         return( syn->syn_oid );
34 }
35
36 LDAP_CONST char *
37 ldap_matchingrule2name( LDAP_MATCHING_RULE * mr )
38 {
39         return( choose_name( mr->mr_names, mr->mr_oid ) );
40 }
41
42 LDAP_CONST char *
43 ldap_attributetype2name( LDAP_ATTRIBUTE_TYPE * at )
44 {
45         return( choose_name( at->at_names, at->at_oid ) );
46 }
47
48 LDAP_CONST char *
49 ldap_objectclass2name( LDAP_OBJECT_CLASS * oc )
50 {
51         return( choose_name( oc->oc_names, oc->oc_oid ) );
52 }
53
54
55 /*
56  * When pretty printing the entities we will be appending to a buffer.
57  * Since checking for overflow, realloc'ing and checking if no error
58  * is extremely boring, we will use a protection layer that will let
59  * us blissfully ignore the error until the end.  This layer is
60  * implemented with the help of the next type.
61  */
62
63 typedef struct safe_string {
64         char * val;
65         ber_len_t size;
66         ber_len_t pos;
67         int at_whsp;
68 } safe_string;
69
70 static safe_string *
71 new_safe_string(int size)
72 {
73         safe_string * ss;
74         
75         ss = LDAP_MALLOC(sizeof(safe_string));
76         if ( !ss )
77                 return(NULL);
78
79         ss->val = LDAP_MALLOC(size);
80         if ( !ss->val ) {
81                 LDAP_FREE(ss);
82                 return(NULL);
83         }
84
85         ss->size = size;
86         ss->pos = 0;
87         ss->at_whsp = 0;
88
89         return ss;
90 }
91
92 static void
93 safe_string_free(safe_string * ss)
94 {
95         if ( !ss )
96                 return;
97         LDAP_FREE(ss->val);
98         LDAP_FREE(ss);
99 }
100
101 static char *
102 safe_string_val(safe_string * ss)
103 {
104         ss->val[ss->pos] = '\0';
105         return(ss->val);
106 }
107
108 static int
109 append_to_safe_string(safe_string * ss, char * s)
110 {
111         int l = strlen(s);
112         char * temp;
113
114         /*
115          * Some runaway process is trying to append to a string that
116          * overflowed and we could not extend.
117          */
118         if ( !ss->val )
119                 return -1;
120
121         /* We always make sure there is at least one position available */
122         if ( ss->pos + l >= ss->size-1 ) {
123                 ss->size *= 2;
124                 temp = LDAP_REALLOC(ss->val, ss->size);
125                 if ( !temp ) {
126                         /* Trouble, out of memory */
127                         LDAP_FREE(ss->val);
128                         return -1;
129                 }
130                 ss->val = temp;
131         }
132         strncpy(&ss->val[ss->pos], s, l);
133         ss->pos += l;
134         if ( ss->pos > 0 && isspace(ss->val[ss->pos-1]) )
135                 ss->at_whsp = 1;
136         else
137                 ss->at_whsp = 0;
138
139         return 0;
140 }
141
142 static int
143 print_literal(safe_string *ss, char *s)
144 {
145         return(append_to_safe_string(ss,s));
146 }
147
148 static int
149 print_whsp(safe_string *ss)
150 {
151         if ( ss->at_whsp )
152                 return(append_to_safe_string(ss,""));
153         else
154                 return(append_to_safe_string(ss," "));
155 }
156
157 static int
158 print_numericoid(safe_string *ss, char *s)
159 {
160         if ( s )
161                 return(append_to_safe_string(ss,s));
162         else
163                 return(append_to_safe_string(ss,""));
164 }
165
166 /* This one is identical to print_qdescr */
167 static int
168 print_qdstring(safe_string *ss, char *s)
169 {
170         print_whsp(ss);
171         print_literal(ss,"'");
172         append_to_safe_string(ss,s);
173         print_literal(ss,"'");
174         return(print_whsp(ss));
175 }
176
177 static int
178 print_qdescr(safe_string *ss, char *s)
179 {
180         print_whsp(ss);
181         print_literal(ss,"'");
182         append_to_safe_string(ss,s);
183         print_literal(ss,"'");
184         return(print_whsp(ss));
185 }
186
187 static int
188 print_qdescrlist(safe_string *ss, char **sa)
189 {
190         char **sp;
191         int ret = 0;
192         
193         for (sp=sa; *sp; sp++) {
194                 ret = print_qdescr(ss,*sp);
195         }
196         /* If the list was empty, we return zero that is potentially
197          * incorrect, but since we will be still appending things, the
198          * overflow will be detected later.  Maybe FIX.
199          */
200         return(ret);
201 }
202
203 static int
204 print_qdescrs(safe_string *ss, char **sa)
205 {
206         /* The only way to represent an empty list is as a qdescrlist
207          * so, if the list is empty we treat it as a long list.
208          * Really, this is what the syntax mandates.  We should not
209          * be here if the list was empty, but if it happens, a label
210          * has already been output and we cannot undo it.
211          */
212         if ( !sa[0] || ( sa[0] && sa[1] ) ) {
213                 print_whsp(ss);
214                 print_literal(ss,"("/*)*/);
215                 print_qdescrlist(ss,sa);
216                 print_literal(ss,/*(*/")");
217                 return(print_whsp(ss));
218         } else {
219           return(print_qdescr(ss,*sa));
220         }
221 }
222
223 static int
224 print_woid(safe_string *ss, char *s)
225 {
226         print_whsp(ss);
227         append_to_safe_string(ss,s);
228         return print_whsp(ss);
229 }
230
231 static int
232 print_oidlist(safe_string *ss, char **sa)
233 {
234         char **sp;
235
236         for (sp=sa; *(sp+1); sp++) {
237                 print_woid(ss,*sp);
238                 print_literal(ss,"$");
239         }
240         return(print_woid(ss,*sp));
241 }
242
243 static int
244 print_oids(safe_string *ss, char **sa)
245 {
246         if ( sa[0] && sa[1] ) {
247                 print_literal(ss,"("/*)*/);
248                 print_oidlist(ss,sa);
249                 print_whsp(ss);
250                 return(print_literal(ss,/*(*/")"));
251         } else {
252                 return(print_woid(ss,*sa));
253         }
254 }
255
256 static int
257 print_noidlen(safe_string *ss, char *s, int l)
258 {
259         char buf[64];
260         int ret;
261
262         ret = print_numericoid(ss,s);
263         if ( l ) {
264                 sprintf(buf,"{%d}",l);
265                 ret = print_literal(ss,buf);
266         }
267         return(ret);
268 }
269
270 static int
271 print_extensions(safe_string *ss, LDAP_SCHEMA_EXTENSION_ITEM **extensions)
272 {
273         LDAP_SCHEMA_EXTENSION_ITEM **ext;
274
275         if ( extensions ) {
276                 print_whsp(ss);
277                 for ( ext = extensions; *ext != NULL; ext++ ) {
278                         print_literal(ss, (*ext)->lsei_name);
279                         print_whsp(ss);
280                         /* Should be print_qdstrings */
281                         print_qdescrs(ss, (*ext)->lsei_values);
282                         print_whsp(ss);
283                 }
284         }
285
286         return 0;
287 }
288
289 char *
290 ldap_syntax2str( const LDAP_SYNTAX * syn )
291 {
292         safe_string * ss;
293         char * retstring;
294         
295         ss = new_safe_string(256);
296         if ( !ss )
297                 return NULL;
298
299         print_literal(ss,"("/*)*/);
300         print_whsp(ss);
301
302         print_numericoid(ss, syn->syn_oid);
303         print_whsp(ss);
304
305         if ( syn->syn_desc ) {
306                 print_literal(ss,"DESC");
307                 print_qdstring(ss,syn->syn_desc);
308         }
309
310         print_whsp(ss);
311
312         print_extensions(ss, syn->syn_extensions);
313
314         print_literal(ss,/*(*/ ")");
315
316         retstring = LDAP_STRDUP(safe_string_val(ss));
317         safe_string_free(ss);
318         return(retstring);
319 }
320
321 char *
322 ldap_matchingrule2str( const LDAP_MATCHING_RULE * mr )
323 {
324         safe_string * ss;
325         char * retstring;
326         
327         ss = new_safe_string(256);
328         if ( !ss )
329                 return NULL;
330
331         print_literal(ss,"(" /*)*/);
332         print_whsp(ss);
333
334         print_numericoid(ss, mr->mr_oid);
335         print_whsp(ss);
336
337         if ( mr->mr_names ) {
338                 print_literal(ss,"NAME");
339                 print_qdescrs(ss,mr->mr_names);
340         }
341
342         if ( mr->mr_desc ) {
343                 print_literal(ss,"DESC");
344                 print_qdstring(ss,mr->mr_desc);
345         }
346
347         if ( mr->mr_obsolete == LDAP_SCHEMA_YES ) {
348                 print_literal(ss, "OBSOLETE");
349                 print_whsp(ss);
350         }
351
352         if ( mr->mr_syntax_oid ) {
353                 print_literal(ss,"SYNTAX");
354                 print_whsp(ss);
355                 print_literal(ss, mr->mr_syntax_oid);
356                 print_whsp(ss);
357         }
358
359         print_whsp(ss);
360
361         print_extensions(ss, mr->mr_extensions);
362
363         print_literal(ss,/*(*/")");
364
365         retstring = LDAP_STRDUP(safe_string_val(ss));
366         safe_string_free(ss);
367         return(retstring);
368 }
369
370 char *
371 ldap_objectclass2str( const LDAP_OBJECT_CLASS * oc )
372 {
373         safe_string * ss;
374         char * retstring;
375         
376         ss = new_safe_string(256);
377         if ( !ss )
378                 return NULL;
379
380         print_literal(ss,"("/*)*/);
381         print_whsp(ss);
382
383         print_numericoid(ss, oc->oc_oid);
384         print_whsp(ss);
385
386         if ( oc->oc_names ) {
387                 print_literal(ss,"NAME");
388                 print_qdescrs(ss,oc->oc_names);
389         }
390
391         if ( oc->oc_desc ) {
392                 print_literal(ss,"DESC");
393                 print_qdstring(ss,oc->oc_desc);
394         }
395
396         if ( oc->oc_obsolete == LDAP_SCHEMA_YES ) {
397                 print_literal(ss, "OBSOLETE");
398                 print_whsp(ss);
399         }
400
401         if ( oc->oc_sup_oids ) {
402                 print_literal(ss,"SUP");
403                 print_whsp(ss);
404                 print_oids(ss,oc->oc_sup_oids);
405                 print_whsp(ss);
406         }
407
408         switch (oc->oc_kind) {
409         case LDAP_SCHEMA_ABSTRACT:
410                 print_literal(ss,"ABSTRACT");
411                 break;
412         case LDAP_SCHEMA_STRUCTURAL:
413                 print_literal(ss,"STRUCTURAL");
414                 break;
415         case LDAP_SCHEMA_AUXILIARY:
416                 print_literal(ss,"AUXILIARY");
417                 break;
418         default:
419                 print_literal(ss,"KIND-UNKNOWN");
420                 break;
421         }
422         print_whsp(ss);
423         
424         if ( oc->oc_at_oids_must ) {
425                 print_literal(ss,"MUST");
426                 print_whsp(ss);
427                 print_oids(ss,oc->oc_at_oids_must);
428                 print_whsp(ss);
429         }
430
431         if ( oc->oc_at_oids_may ) {
432                 print_literal(ss,"MAY");
433                 print_whsp(ss);
434                 print_oids(ss,oc->oc_at_oids_may);
435                 print_whsp(ss);
436         }
437
438         print_whsp(ss);
439
440         print_extensions(ss, oc->oc_extensions);
441
442         print_literal(ss, /*(*/")");
443
444         retstring = LDAP_STRDUP(safe_string_val(ss));
445         safe_string_free(ss);
446         return(retstring);
447 }
448
449 char *
450 ldap_attributetype2str( const LDAP_ATTRIBUTE_TYPE * at )
451 {
452         safe_string * ss;
453         char * retstring;
454         
455         ss = new_safe_string(256);
456         if ( !ss )
457                 return NULL;
458
459         print_literal(ss,"("/*)*/);
460         print_whsp(ss);
461
462         print_numericoid(ss, at->at_oid);
463         print_whsp(ss);
464
465         if ( at->at_names ) {
466                 print_literal(ss,"NAME");
467                 print_qdescrs(ss,at->at_names);
468         }
469
470         if ( at->at_desc ) {
471                 print_literal(ss,"DESC");
472                 print_qdstring(ss,at->at_desc);
473         }
474
475         if ( at->at_obsolete == LDAP_SCHEMA_YES ) {
476                 print_literal(ss, "OBSOLETE");
477                 print_whsp(ss);
478         }
479
480         if ( at->at_sup_oid ) {
481                 print_literal(ss,"SUP");
482                 print_woid(ss,at->at_sup_oid);
483         }
484
485         if ( at->at_equality_oid ) {
486                 print_literal(ss,"EQUALITY");
487                 print_woid(ss,at->at_equality_oid);
488         }
489
490         if ( at->at_ordering_oid ) {
491                 print_literal(ss,"ORDERING");
492                 print_woid(ss,at->at_ordering_oid);
493         }
494
495         if ( at->at_substr_oid ) {
496                 print_literal(ss,"SUBSTR");
497                 print_woid(ss,at->at_substr_oid);
498         }
499
500         if ( at->at_syntax_oid ) {
501                 print_literal(ss,"SYNTAX");
502                 print_whsp(ss);
503                 print_noidlen(ss,at->at_syntax_oid,at->at_syntax_len);
504                 print_whsp(ss);
505         }
506
507         if ( at->at_single_value == LDAP_SCHEMA_YES ) {
508                 print_literal(ss,"SINGLE-VALUE");
509                 print_whsp(ss);
510         }
511
512         if ( at->at_collective == LDAP_SCHEMA_YES ) {
513                 print_literal(ss,"COLLECTIVE");
514                 print_whsp(ss);
515         }
516
517         if ( at->at_no_user_mod == LDAP_SCHEMA_YES ) {
518                 print_literal(ss,"NO-USER-MODIFICATION");
519                 print_whsp(ss);
520         }
521
522         if ( at->at_usage != LDAP_SCHEMA_USER_APPLICATIONS ) {
523                 print_literal(ss,"USAGE");
524                 print_whsp(ss);
525                 switch (at->at_usage) {
526                 case LDAP_SCHEMA_DIRECTORY_OPERATION:
527                         print_literal(ss,"directoryOperation");
528                         break;
529                 case LDAP_SCHEMA_DISTRIBUTED_OPERATION:
530                         print_literal(ss,"distributedOperation");
531                         break;
532                 case LDAP_SCHEMA_DSA_OPERATION:
533                         print_literal(ss,"dSAOperation");
534                         break;
535                 default:
536                         print_literal(ss,"UNKNOWN");
537                         break;
538                 }
539         }
540         
541         print_whsp(ss);
542
543         print_extensions(ss, at->at_extensions);
544
545         print_literal(ss,/*(*/")");
546
547         retstring = LDAP_STRDUP(safe_string_val(ss));
548         safe_string_free(ss);
549         return(retstring);
550 }
551
552 /*
553  * Now come the parsers.  There is one parser for each entity type:
554  * objectclasses, attributetypes, etc.
555  *
556  * Each of them is written as a recursive-descent parser, except that
557  * none of them is really recursive.  But the idea is kept: there
558  * is one routine per non-terminal that eithers gobbles lexical tokens
559  * or calls lower-level routines, etc.
560  *
561  * The scanner is implemented in the routine get_token.  Actually,
562  * get_token is more than a scanner and will return tokens that are
563  * in fact non-terminals in the grammar.  So you can see the whole
564  * approach as the combination of a low-level bottom-up recognizer
565  * combined with a scanner and a number of top-down parsers.  Or just
566  * consider that the real grammars recognized by the parsers are not
567  * those of the standards.  As a matter of fact, our parsers are more
568  * liberal than the spec when there is no ambiguity.
569  *
570  * The difference is pretty academic (modulo bugs or incorrect
571  * interpretation of the specs).
572  */
573
574 #define TK_NOENDQUOTE   -2
575 #define TK_OUTOFMEM     -1
576 #define TK_EOS          0
577 #define TK_UNEXPCHAR    1
578 #define TK_BAREWORD     2
579 #define TK_QDSTRING     3
580 #define TK_LEFTPAREN    4
581 #define TK_RIGHTPAREN   5
582 #define TK_DOLLAR       6
583 #define TK_QDESCR       TK_QDSTRING
584
585 struct token {
586         int type;
587         char *sval;
588 };
589
590 static int
591 get_token(const char ** sp, char ** token_val)
592 {
593         int kind;
594         const char * p;
595         const char * q;
596         char * res;
597
598         *token_val = NULL;
599         switch (**sp) {
600         case '\0':
601                 kind = TK_EOS;
602                 (*sp)++;
603                 break;
604         case '(':
605                 kind = TK_LEFTPAREN;
606                 (*sp)++;
607                 break;
608         case ')':
609                 kind = TK_RIGHTPAREN;
610                 (*sp)++;
611                 break;
612         case '$':
613                 kind = TK_DOLLAR;
614                 (*sp)++;
615                 break;
616         case '\'':
617                 kind = TK_QDSTRING;
618                 (*sp)++;
619                 p = *sp;
620                 while ( **sp != '\'' && **sp != '\0' )
621                         (*sp)++;
622                 if ( **sp == '\'' ) {
623                         q = *sp;
624                         res = LDAP_MALLOC(q-p+1);
625                         if ( !res ) {
626                                 kind = TK_OUTOFMEM;
627                         } else {
628                                 strncpy(res,p,q-p);
629                                 res[q-p] = '\0';
630                                 *token_val = res;
631                         }
632                         (*sp)++;
633                 } else {
634                         kind = TK_NOENDQUOTE;
635                 }
636                 break;
637         default:
638                 kind = TK_BAREWORD;
639                 p = *sp;
640                 while ( !isspace(**sp) &&
641                         **sp != '(' &&
642                         **sp != ')' &&
643                         **sp != '$' &&
644                         **sp != '\'' &&
645                         **sp != '\0' )
646                         (*sp)++;
647                 q = *sp;
648                 res = LDAP_MALLOC(q-p+1);
649                 if ( !res ) {
650                         kind = TK_OUTOFMEM;
651                 } else {
652                         strncpy(res,p,q-p);
653                         res[q-p] = '\0';
654                         *token_val = res;
655                 }
656                 break;
657 /*              kind = TK_UNEXPCHAR; */
658 /*              break; */
659         }
660         
661         return kind;
662 }
663
664 /* Gobble optional whitespace */
665 static void
666 parse_whsp(const char **sp)
667 {
668         while (isspace(**sp))
669                 (*sp)++;
670 }
671
672 /* TBC:!!
673  * General note for all parsers: to guarantee the algorithm halts they
674  * must always advance the pointer even when an error is found.  For
675  * this one is not that important since an error here is fatal at the
676  * upper layers, but it is a simple strategy that will not get in
677  * endless loops.
678  */
679
680 /* Parse a sequence of dot-separated decimal strings */
681 static char *
682 parse_numericoid(const char **sp, int *code, const int flags)
683 {
684         char * res;
685         const char * start = *sp;
686         int len;
687         int quoted = 0;
688
689         /* Netscape puts the SYNTAX value in quotes (incorrectly) */
690         if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && **sp == '\'' ) {
691                 quoted = 1;
692                 (*sp)++;
693                 start++;
694         }
695         /* Each iteration of this loop gets one decimal string */
696         while (**sp) {
697                 if ( !isdigit(**sp) ) {
698                         /*
699                          * Initial char is not a digit or char after dot is
700                          * not a digit
701                          */
702                         *code = LDAP_SCHERR_NODIGIT;
703                         return NULL;
704                 }
705                 (*sp)++;
706                 while ( isdigit(**sp) )
707                         (*sp)++;
708                 if ( **sp != '.' )
709                         break;
710                 /* Otherwise, gobble the dot and loop again */
711                 (*sp)++;
712         }
713         /* Now *sp points at the char past the numericoid. Perfect. */
714         len = *sp - start;
715         res = LDAP_MALLOC(len+1);
716         if (!res) {
717                 *code = LDAP_SCHERR_OUTOFMEM;
718                 return(NULL);
719         }
720         strncpy(res,start,len);
721         res[len] = '\0';
722         if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && quoted ) {
723                 if ( **sp == '\'' ) {
724                         (*sp)++;
725                 } else {
726                         *code = LDAP_SCHERR_UNEXPTOKEN;
727                         LDAP_FREE(res);
728                         return NULL;
729                 }
730         }
731         return(res);
732 }
733
734 /* Parse a qdescr or a list of them enclosed in () */
735 static char **
736 parse_qdescrs(const char **sp, int *code)
737 {
738         char ** res;
739         char ** res1;
740         int kind;
741         char * sval;
742         int size;
743         int pos;
744
745         parse_whsp(sp);
746         kind = get_token(sp,&sval);
747         if ( kind == TK_LEFTPAREN ) {
748                 /* Let's presume there will be at least 2 entries */
749                 size = 3;
750                 res = LDAP_CALLOC(3,sizeof(char *));
751                 if ( !res ) {
752                         *code = LDAP_SCHERR_OUTOFMEM;
753                         return NULL;
754                 }
755                 pos = 0;
756                 while (1) {
757                         parse_whsp(sp);
758                         kind = get_token(sp,&sval);
759                         if ( kind == TK_RIGHTPAREN )
760                                 break;
761                         if ( kind == TK_QDESCR ) {
762                                 if ( pos == size-2 ) {
763                                         size++;
764                                         res1 = LDAP_REALLOC(res,size*sizeof(char *));
765                                         if ( !res1 ) {
766                                                 LDAP_VFREE(res);
767                                                 LDAP_FREE(sval);
768                                                 *code = LDAP_SCHERR_OUTOFMEM;
769                                                 return(NULL);
770                                         }
771                                         res = res1;
772                                 }
773                                 res[pos] = sval;
774                                 pos++;
775                                 parse_whsp(sp);
776                         } else {
777                                 LDAP_VFREE(res);
778                                 LDAP_FREE(sval);
779                                 *code = LDAP_SCHERR_UNEXPTOKEN;
780                                 return(NULL);
781                         }
782                 }
783                 res[pos] = NULL;
784                 parse_whsp(sp);
785                 return(res);
786         } else if ( kind == TK_QDESCR ) {
787                 res = LDAP_CALLOC(2,sizeof(char *));
788                 if ( !res ) {
789                         *code = LDAP_SCHERR_OUTOFMEM;
790                         return NULL;
791                 }
792                 res[0] = sval;
793                 res[1] = NULL;
794                 parse_whsp(sp);
795                 return res;
796         } else {
797                 LDAP_FREE(sval);
798                 *code = LDAP_SCHERR_BADNAME;
799                 return NULL;
800         }
801 }
802
803 /* Parse a woid */
804 static char *
805 parse_woid(const char **sp, int *code)
806 {
807         char * sval;
808         int kind;
809
810         parse_whsp(sp);
811         kind = get_token(sp, &sval);
812         if ( kind != TK_BAREWORD ) {
813                 LDAP_FREE(sval);
814                 *code = LDAP_SCHERR_UNEXPTOKEN;
815                 return NULL;
816         }
817         parse_whsp(sp);
818         return sval;
819 }
820
821 /* Parse a noidlen */
822 static char *
823 parse_noidlen(const char **sp, int *code, int *len, int allow_quoted)
824 {
825         char * sval;
826         int quoted = 0;
827
828         *len = 0;
829         /* Netscape puts the SYNTAX value in quotes (incorrectly) */
830         if ( allow_quoted && **sp == '\'' ) {
831                 quoted = 1;
832                 (*sp)++;
833         }
834         sval = parse_numericoid(sp, code, 0);
835         if ( !sval ) {
836                 return NULL;
837         }
838         if ( **sp == '{' /*}*/ ) {
839                 (*sp)++;
840                 *len = atoi(*sp);
841                 while ( isdigit(**sp) )
842                         (*sp)++;
843                 if ( **sp != /*{*/ '}' ) {
844                         *code = LDAP_SCHERR_UNEXPTOKEN;
845                         LDAP_FREE(sval);
846                         return NULL;
847                 }
848                 (*sp)++;
849         }               
850         if ( allow_quoted && quoted ) {
851                 if ( **sp == '\'' ) {
852                         (*sp)++;
853                 } else {
854                         *code = LDAP_SCHERR_UNEXPTOKEN;
855                         LDAP_FREE(sval);
856                         return NULL;
857                 }
858         }
859         return sval;
860 }
861
862 /*
863  * Next routine will accept a qdstring in place of an oid if
864  * allow_quoted is set.  This is necessary to interoperate with
865  * Netscape Directory server that will improperly quote each oid (at
866  * least those of the descr kind) in the SUP clause.
867  */
868
869 /* Parse a woid or a $-separated list of them enclosed in () */
870 static char **
871 parse_oids(const char **sp, int *code, const int allow_quoted)
872 {
873         char ** res;
874         char ** res1;
875         int kind;
876         char * sval;
877         int size;
878         int pos;
879
880         /*
881          * Strictly speaking, doing this here accepts whsp before the
882          * ( at the begining of an oidlist, but this is harmless.  Also,
883          * we are very liberal in what we accept as an OID.  Maybe
884          * refine later.
885          */
886         parse_whsp(sp);
887         kind = get_token(sp,&sval);
888         if ( kind == TK_LEFTPAREN ) {
889                 /* Let's presume there will be at least 2 entries */
890                 size = 3;
891                 res = LDAP_CALLOC(3,sizeof(char *));
892                 if ( !res ) {
893                         *code = LDAP_SCHERR_OUTOFMEM;
894                         return NULL;
895                 }
896                 pos = 0;
897                 parse_whsp(sp);
898                 kind = get_token(sp,&sval);
899                 if ( kind == TK_BAREWORD ||
900                      ( allow_quoted && kind == TK_QDSTRING ) ) {
901                         res[pos] = sval;
902                         pos++;
903                 } else {
904                         *code = LDAP_SCHERR_UNEXPTOKEN;
905                         LDAP_FREE(sval);
906                         LDAP_VFREE(res);
907                         return NULL;
908                 }
909                 parse_whsp(sp);
910                 while (1) {
911                         kind = get_token(sp,&sval);
912                         if ( kind == TK_RIGHTPAREN )
913                                 break;
914                         if ( kind == TK_DOLLAR ) {
915                                 parse_whsp(sp);
916                                 kind = get_token(sp,&sval);
917                                 if ( kind == TK_BAREWORD ||
918                                      ( allow_quoted &&
919                                        kind == TK_QDSTRING ) ) {
920                                         if ( pos == size-2 ) {
921                                                 size++;
922                                                 res1 = LDAP_REALLOC(res,size*sizeof(char *));
923                                                 if ( !res1 ) {
924                                                         LDAP_FREE(sval);
925                                                         LDAP_VFREE(res);
926                                                         *code = LDAP_SCHERR_OUTOFMEM;
927                                                         return(NULL);
928                                                 }
929                                                 res = res1;
930                                         }
931                                         res[pos] = sval;
932                                         pos++;
933                                 } else {
934                                         *code = LDAP_SCHERR_UNEXPTOKEN;
935                                         LDAP_FREE(sval);
936                                         LDAP_VFREE(res);
937                                         return NULL;
938                                 }
939                                 parse_whsp(sp);
940                         } else {
941                                 *code = LDAP_SCHERR_UNEXPTOKEN;
942                                 LDAP_FREE(sval);
943                                 LDAP_VFREE(res);
944                                 return NULL;
945                         }
946                 }
947                 res[pos] = NULL;
948                 parse_whsp(sp);
949                 return(res);
950         } else if ( kind == TK_BAREWORD ||
951                     ( allow_quoted && kind == TK_QDSTRING ) ) {
952                 res = LDAP_CALLOC(2,sizeof(char *));
953                 if ( !res ) {
954                         LDAP_FREE(sval);
955                         *code = LDAP_SCHERR_OUTOFMEM;
956                         return NULL;
957                 }
958                 res[0] = sval;
959                 res[1] = NULL;
960                 parse_whsp(sp);
961                 return res;
962         } else {
963                 LDAP_FREE(sval);
964                 *code = LDAP_SCHERR_BADNAME;
965                 return NULL;
966         }
967 }
968
969 static int
970 add_extension(LDAP_SCHEMA_EXTENSION_ITEM ***extensions,
971               char * name, char ** values)
972 {
973         int n;
974         LDAP_SCHEMA_EXTENSION_ITEM **tmp, *ext;
975
976         ext = LDAP_CALLOC(1, sizeof(LDAP_SCHEMA_EXTENSION_ITEM));
977         if ( !ext )
978                 return 1;
979         ext->lsei_name = name;
980         ext->lsei_values = values;
981
982         if ( !*extensions ) {
983                 *extensions =
984                   LDAP_CALLOC(2, sizeof(LDAP_SCHEMA_EXTENSION_ITEM *));
985                 if ( !*extensions )
986                   return 1;
987                 n = 0;
988         } else {
989                 for ( n=0; (*extensions)[n] != NULL; n++ )
990                         ;
991                 tmp = LDAP_REALLOC(*extensions,
992                                    (n+2)*sizeof(LDAP_SCHEMA_EXTENSION_ITEM *));
993                 if ( !tmp )
994                         return 1;
995                 *extensions = tmp;
996         }
997         (*extensions)[n] = ext;
998         (*extensions)[n+1] = NULL;
999         return 0;
1000 }
1001
1002 static void
1003 free_extensions(LDAP_SCHEMA_EXTENSION_ITEM **extensions)
1004 {
1005         LDAP_SCHEMA_EXTENSION_ITEM **ext;
1006
1007         if ( extensions ) {
1008                 for ( ext = extensions; *ext != NULL; ext++ ) {
1009                         LDAP_FREE((*ext)->lsei_name);
1010                         LDAP_VFREE((*ext)->lsei_values);
1011                         LDAP_FREE(*ext);
1012                 }
1013                 LDAP_FREE(extensions);
1014         }
1015 }
1016
1017 void
1018 ldap_syntax_free( LDAP_SYNTAX * syn )
1019 {
1020         LDAP_FREE(syn->syn_oid);
1021         LDAP_FREE(syn->syn_desc);
1022         free_extensions(syn->syn_extensions);
1023         LDAP_FREE(syn);
1024 }
1025
1026 LDAP_SYNTAX *
1027 ldap_str2syntax( const char * s, int * code, const char ** errp, const int flags )
1028 {
1029         int kind;
1030         const char * ss = s;
1031         char * sval;
1032         int seen_name = 0;
1033         int seen_desc = 0;
1034         LDAP_SYNTAX * syn;
1035         char ** ext_vals;
1036
1037         if ( !s ) {
1038                 *code = LDAP_SCHERR_EMPTY;
1039                 *errp = "";
1040                 return NULL;
1041         }
1042
1043         *errp = s;
1044         syn = LDAP_CALLOC(1,sizeof(LDAP_SYNTAX));
1045
1046         if ( !syn ) {
1047                 *code = LDAP_SCHERR_OUTOFMEM;
1048                 return NULL;
1049         }
1050
1051         kind = get_token(&ss,&sval);
1052         if ( kind != TK_LEFTPAREN ) {
1053                 LDAP_FREE(sval);
1054                 *code = LDAP_SCHERR_NOLEFTPAREN;
1055                 ldap_syntax_free(syn);
1056                 return NULL;
1057         }
1058
1059         parse_whsp(&ss);
1060         syn->syn_oid = parse_numericoid(&ss,code,0);
1061         if ( !syn->syn_oid ) {
1062                 *errp = ss;
1063                 ldap_syntax_free(syn);
1064                 return NULL;
1065         }
1066         parse_whsp(&ss);
1067
1068         /*
1069          * Beyond this point we will be liberal and accept the items
1070          * in any order.
1071          */
1072         while (1) {
1073                 kind = get_token(&ss,&sval);
1074                 switch (kind) {
1075                 case TK_EOS:
1076                         *code = LDAP_SCHERR_NORIGHTPAREN;
1077                         *errp = ss;
1078                         ldap_syntax_free(syn);
1079                         return NULL;
1080                 case TK_RIGHTPAREN:
1081                         return syn;
1082                 case TK_BAREWORD:
1083                         if ( !strcmp(sval,"NAME") ) {
1084                                 LDAP_FREE(sval);
1085                                 if ( seen_name ) {
1086                                         *code = LDAP_SCHERR_DUPOPT;
1087                                         *errp = ss;
1088                                         ldap_syntax_free(syn);
1089                                         return(NULL);
1090                                 }
1091                                 seen_name = 1;
1092                                 syn->syn_names = parse_qdescrs(&ss,code);
1093                                 if ( !syn->syn_names ) {
1094                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1095                                                 *code = LDAP_SCHERR_BADNAME;
1096                                         *errp = ss;
1097                                         ldap_syntax_free(syn);
1098                                         return NULL;
1099                                 }
1100                         } else if ( !strcmp(sval,"DESC") ) {
1101                                 LDAP_FREE(sval);
1102                                 if ( seen_desc ) {
1103                                         *code = LDAP_SCHERR_DUPOPT;
1104                                         *errp = ss;
1105                                         ldap_syntax_free(syn);
1106                                         return(NULL);
1107                                 }
1108                                 seen_desc = 1;
1109                                 parse_whsp(&ss);
1110                                 kind = get_token(&ss,&sval);
1111                                 if ( kind != TK_QDSTRING ) {
1112                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1113                                         *errp = ss;
1114                                         LDAP_FREE(sval);
1115                                         ldap_syntax_free(syn);
1116                                         return NULL;
1117                                 }
1118                                 syn->syn_desc = sval;
1119                                 parse_whsp(&ss);
1120                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1121                                 /* Should be parse_qdstrings */
1122                                 ext_vals = parse_qdescrs(&ss, code);
1123                                 if ( !ext_vals ) {
1124                                         *errp = ss;
1125                                         ldap_syntax_free(syn);
1126                                         return NULL;
1127                                 }
1128                                 if ( add_extension(&syn->syn_extensions,
1129                                                     sval, ext_vals) ) {
1130                                         *code = LDAP_SCHERR_OUTOFMEM;
1131                                         *errp = ss;
1132                                         LDAP_FREE(sval);
1133                                         ldap_syntax_free(syn);
1134                                         return NULL;
1135                                 }
1136                         } else {
1137                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1138                                 *errp = ss;
1139                                 LDAP_FREE(sval);
1140                                 ldap_syntax_free(syn);
1141                                 return NULL;
1142                         }
1143                         break;
1144                 default:
1145                         *code = LDAP_SCHERR_UNEXPTOKEN;
1146                         *errp = ss;
1147                         LDAP_FREE(sval);
1148                         ldap_syntax_free(syn);
1149                         return NULL;
1150                 }
1151         }
1152 }
1153
1154 void
1155 ldap_matchingrule_free( LDAP_MATCHING_RULE * mr )
1156 {
1157         LDAP_FREE(mr->mr_oid);
1158         LDAP_VFREE(mr->mr_names);
1159         LDAP_FREE(mr->mr_desc);
1160         LDAP_FREE(mr->mr_syntax_oid);
1161         free_extensions(mr->mr_extensions);
1162         LDAP_FREE(mr);
1163 }
1164
1165 LDAP_MATCHING_RULE *
1166 ldap_str2matchingrule( const char * s, int * code, const char ** errp, const int flags )
1167 {
1168         int kind;
1169         const char * ss = s;
1170         char * sval;
1171         int seen_name = 0;
1172         int seen_desc = 0;
1173         int seen_obsolete = 0;
1174         int seen_syntax = 0;
1175         LDAP_MATCHING_RULE * mr;
1176         char ** ext_vals;
1177         const char * savepos;
1178
1179         if ( !s ) {
1180                 *code = LDAP_SCHERR_EMPTY;
1181                 *errp = "";
1182                 return NULL;
1183         }
1184
1185         *errp = s;
1186         mr = LDAP_CALLOC(1,sizeof(LDAP_MATCHING_RULE));
1187
1188         if ( !mr ) {
1189                 *code = LDAP_SCHERR_OUTOFMEM;
1190                 return NULL;
1191         }
1192
1193         kind = get_token(&ss,&sval);
1194         if ( kind != TK_LEFTPAREN ) {
1195                 *code = LDAP_SCHERR_NOLEFTPAREN;
1196                 LDAP_FREE(sval);
1197                 ldap_matchingrule_free(mr);
1198                 return NULL;
1199         }
1200
1201         parse_whsp(&ss);
1202         savepos = ss;
1203         mr->mr_oid = parse_numericoid(&ss,code,flags);
1204         if ( !mr->mr_oid ) {
1205                 if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
1206                         /* Backtracking */
1207                         ss = savepos;
1208                         kind = get_token(&ss,&sval);
1209                         if ( kind == TK_BAREWORD ) {
1210                                 if ( !strcmp(sval, "NAME") ||
1211                                      !strcmp(sval, "DESC") ||
1212                                      !strcmp(sval, "OBSOLETE") ||
1213                                      !strcmp(sval, "SYNTAX") ||
1214                                      !strncmp(sval, "X-", 2) ) {
1215                                         /* Missing OID, backtrack */
1216                                         ss = savepos;
1217                                 } else {
1218                                         /* Non-numerical OID, ignore */
1219                                 }
1220                         }
1221                         LDAP_FREE(sval);
1222                 } else {
1223                         *errp = ss;
1224                         ldap_matchingrule_free(mr);
1225                         return NULL;
1226                 }
1227         }
1228         parse_whsp(&ss);
1229
1230         /*
1231          * Beyond this point we will be liberal and accept the items
1232          * in any order.
1233          */
1234         while (1) {
1235                 kind = get_token(&ss,&sval);
1236                 switch (kind) {
1237                 case TK_EOS:
1238                         *code = LDAP_SCHERR_NORIGHTPAREN;
1239                         *errp = ss;
1240                         ldap_matchingrule_free(mr);
1241                         return NULL;
1242                 case TK_RIGHTPAREN:
1243                         return mr;
1244                 case TK_BAREWORD:
1245                         if ( !strcmp(sval,"NAME") ) {
1246                                 LDAP_FREE(sval);
1247                                 if ( seen_name ) {
1248                                         *code = LDAP_SCHERR_DUPOPT;
1249                                         *errp = ss;
1250                                         ldap_matchingrule_free(mr);
1251                                         return(NULL);
1252                                 }
1253                                 seen_name = 1;
1254                                 mr->mr_names = parse_qdescrs(&ss,code);
1255                                 if ( !mr->mr_names ) {
1256                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1257                                                 *code = LDAP_SCHERR_BADNAME;
1258                                         *errp = ss;
1259                                         ldap_matchingrule_free(mr);
1260                                         return NULL;
1261                                 }
1262                         } else if ( !strcmp(sval,"DESC") ) {
1263                                 LDAP_FREE(sval);
1264                                 if ( seen_desc ) {
1265                                         *code = LDAP_SCHERR_DUPOPT;
1266                                         *errp = ss;
1267                                         ldap_matchingrule_free(mr);
1268                                         return(NULL);
1269                                 }
1270                                 seen_desc = 1;
1271                                 parse_whsp(&ss);
1272                                 kind = get_token(&ss,&sval);
1273                                 if ( kind != TK_QDSTRING ) {
1274                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1275                                         *errp = ss;
1276                                         LDAP_FREE(sval);
1277                                         ldap_matchingrule_free(mr);
1278                                         return NULL;
1279                                 }
1280                                 mr->mr_desc = sval;
1281                                 parse_whsp(&ss);
1282                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1283                                 LDAP_FREE(sval);
1284                                 if ( seen_obsolete ) {
1285                                         *code = LDAP_SCHERR_DUPOPT;
1286                                         *errp = ss;
1287                                         ldap_matchingrule_free(mr);
1288                                         return(NULL);
1289                                 }
1290                                 seen_obsolete = 1;
1291                                 mr->mr_obsolete = LDAP_SCHEMA_YES;
1292                                 parse_whsp(&ss);
1293                         } else if ( !strcmp(sval,"SYNTAX") ) {
1294                                 LDAP_FREE(sval);
1295                                 if ( seen_syntax ) {
1296                                         *code = LDAP_SCHERR_DUPOPT;
1297                                         *errp = ss;
1298                                         ldap_matchingrule_free(mr);
1299                                         return(NULL);
1300                                 }
1301                                 seen_syntax = 1;
1302                                 parse_whsp(&ss);
1303                                 mr->mr_syntax_oid =
1304                                         parse_numericoid(&ss,code,flags);
1305                                 if ( !mr->mr_syntax_oid ) {
1306                                         *errp = ss;
1307                                         ldap_matchingrule_free(mr);
1308                                         return NULL;
1309                                 }
1310                                 parse_whsp(&ss);
1311                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1312                                 /* Should be parse_qdstrings */
1313                                 ext_vals = parse_qdescrs(&ss, code);
1314                                 if ( !ext_vals ) {
1315                                         *errp = ss;
1316                                         ldap_matchingrule_free(mr);
1317                                         return NULL;
1318                                 }
1319                                 if ( add_extension(&mr->mr_extensions,
1320                                                     sval, ext_vals) ) {
1321                                         *code = LDAP_SCHERR_OUTOFMEM;
1322                                         *errp = ss;
1323                                         LDAP_FREE(sval);
1324                                         ldap_matchingrule_free(mr);
1325                                         return NULL;
1326                                 }
1327                         } else {
1328                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1329                                 *errp = ss;
1330                                 LDAP_FREE(sval);
1331                                 ldap_matchingrule_free(mr);
1332                                 return NULL;
1333                         }
1334                         break;
1335                 default:
1336                         *code = LDAP_SCHERR_UNEXPTOKEN;
1337                         *errp = ss;
1338                         LDAP_FREE(sval);
1339                         ldap_matchingrule_free(mr);
1340                         return NULL;
1341                 }
1342         }
1343 }
1344
1345 void
1346 ldap_attributetype_free(LDAP_ATTRIBUTE_TYPE * at)
1347 {
1348         LDAP_FREE(at->at_oid);
1349         LDAP_VFREE(at->at_names);
1350         LDAP_FREE(at->at_desc);
1351         LDAP_FREE(at->at_sup_oid);
1352         LDAP_FREE(at->at_equality_oid);
1353         LDAP_FREE(at->at_ordering_oid);
1354         LDAP_FREE(at->at_substr_oid);
1355         LDAP_FREE(at->at_syntax_oid);
1356         free_extensions(at->at_extensions);
1357         LDAP_FREE(at);
1358 }
1359
1360 LDAP_ATTRIBUTE_TYPE *
1361 ldap_str2attributetype( const char * s, int * code, const char ** errp, const int flags )
1362 {
1363         int kind;
1364         const char * ss = s;
1365         char * sval;
1366         int seen_name = 0;
1367         int seen_desc = 0;
1368         int seen_obsolete = 0;
1369         int seen_sup = 0;
1370         int seen_equality = 0;
1371         int seen_ordering = 0;
1372         int seen_substr = 0;
1373         int seen_syntax = 0;
1374         int seen_usage = 0;
1375         LDAP_ATTRIBUTE_TYPE * at;
1376         char ** ext_vals;
1377         const char * savepos;
1378
1379         if ( !s ) {
1380                 *code = LDAP_SCHERR_EMPTY;
1381                 *errp = "";
1382                 return NULL;
1383         }
1384
1385         *errp = s;
1386         at = LDAP_CALLOC(1,sizeof(LDAP_ATTRIBUTE_TYPE));
1387
1388         if ( !at ) {
1389                 *code = LDAP_SCHERR_OUTOFMEM;
1390                 return NULL;
1391         }
1392
1393         kind = get_token(&ss,&sval);
1394         if ( kind != TK_LEFTPAREN ) {
1395                 *code = LDAP_SCHERR_NOLEFTPAREN;
1396                 LDAP_FREE(sval);
1397                 ldap_attributetype_free(at);
1398                 return NULL;
1399         }
1400
1401         /*
1402          * Definitions MUST begin with an OID in the numericoid format.
1403          * However, this routine is used by clients to parse the response
1404          * from servers and very well known servers will provide an OID
1405          * in the wrong format or even no OID at all.  We do our best to
1406          * extract info from those servers.
1407          */
1408         parse_whsp(&ss);
1409         savepos = ss;
1410         at->at_oid = parse_numericoid(&ss,code,0);
1411         if ( !at->at_oid ) {
1412                 if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
1413                         /* Backtracking */
1414                         ss = savepos;
1415                         kind = get_token(&ss,&sval);
1416                         if ( kind == TK_BAREWORD ) {
1417                                 if ( !strcmp(sval, "NAME") ||
1418                                      !strcmp(sval, "DESC") ||
1419                                      !strcmp(sval, "OBSOLETE") ||
1420                                      !strcmp(sval, "SUP") ||
1421                                      !strcmp(sval, "EQUALITY") ||
1422                                      !strcmp(sval, "ORDERING") ||
1423                                      !strcmp(sval, "SUBSTR") ||
1424                                      !strcmp(sval, "SYNTAX") ||
1425                                      !strcmp(sval, "SINGLE-VALUE") ||
1426                                      !strcmp(sval, "COLLECTIVE") ||
1427                                      !strcmp(sval, "NO-USER-MODIFICATION") ||
1428                                      !strcmp(sval, "USAGE") ||
1429                                      !strncmp(sval, "X-", 2) ) {
1430                                         /* Missing OID, backtrack */
1431                                         ss = savepos;
1432                                 } else {
1433                                         /* Non-numerical OID, ignore */
1434                                 }
1435                         }
1436                         LDAP_FREE(sval);
1437                 } else {
1438                         *errp = ss;
1439                         ldap_attributetype_free(at);
1440                         return NULL;
1441                 }
1442         }
1443         parse_whsp(&ss);
1444
1445         /*
1446          * Beyond this point we will be liberal and accept the items
1447          * in any order.
1448          */
1449         while (1) {
1450                 kind = get_token(&ss,&sval);
1451                 switch (kind) {
1452                 case TK_EOS:
1453                         *code = LDAP_SCHERR_NORIGHTPAREN;
1454                         *errp = ss;
1455                         ldap_attributetype_free(at);
1456                         return NULL;
1457                 case TK_RIGHTPAREN:
1458                         return at;
1459                 case TK_BAREWORD:
1460                         if ( !strcmp(sval,"NAME") ) {
1461                                 LDAP_FREE(sval);
1462                                 if ( seen_name ) {
1463                                         *code = LDAP_SCHERR_DUPOPT;
1464                                         *errp = ss;
1465                                         ldap_attributetype_free(at);
1466                                         return(NULL);
1467                                 }
1468                                 seen_name = 1;
1469                                 at->at_names = parse_qdescrs(&ss,code);
1470                                 if ( !at->at_names ) {
1471                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1472                                                 *code = LDAP_SCHERR_BADNAME;
1473                                         *errp = ss;
1474                                         ldap_attributetype_free(at);
1475                                         return NULL;
1476                                 }
1477                         } else if ( !strcmp(sval,"DESC") ) {
1478                                 LDAP_FREE(sval);
1479                                 if ( seen_desc ) {
1480                                         *code = LDAP_SCHERR_DUPOPT;
1481                                         *errp = ss;
1482                                         ldap_attributetype_free(at);
1483                                         return(NULL);
1484                                 }
1485                                 seen_desc = 1;
1486                                 parse_whsp(&ss);
1487                                 kind = get_token(&ss,&sval);
1488                                 if ( kind != TK_QDSTRING ) {
1489                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1490                                         *errp = ss;
1491                                         LDAP_FREE(sval);
1492                                         ldap_attributetype_free(at);
1493                                         return NULL;
1494                                 }
1495                                 at->at_desc = sval;
1496                                 parse_whsp(&ss);
1497                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1498                                 LDAP_FREE(sval);
1499                                 if ( seen_obsolete ) {
1500                                         *code = LDAP_SCHERR_DUPOPT;
1501                                         *errp = ss;
1502                                         ldap_attributetype_free(at);
1503                                         return(NULL);
1504                                 }
1505                                 seen_obsolete = 1;
1506                                 at->at_obsolete = LDAP_SCHEMA_YES;
1507                                 parse_whsp(&ss);
1508                         } else if ( !strcmp(sval,"SUP") ) {
1509                                 LDAP_FREE(sval);
1510                                 if ( seen_sup ) {
1511                                         *code = LDAP_SCHERR_DUPOPT;
1512                                         *errp = ss;
1513                                         ldap_attributetype_free(at);
1514                                         return(NULL);
1515                                 }
1516                                 seen_sup = 1;
1517                                 at->at_sup_oid = parse_woid(&ss,code);
1518                                 if ( !at->at_sup_oid ) {
1519                                         *errp = ss;
1520                                         ldap_attributetype_free(at);
1521                                         return NULL;
1522                                 }
1523                         } else if ( !strcmp(sval,"EQUALITY") ) {
1524                                 LDAP_FREE(sval);
1525                                 if ( seen_equality ) {
1526                                         *code = LDAP_SCHERR_DUPOPT;
1527                                         *errp = ss;
1528                                         ldap_attributetype_free(at);
1529                                         return(NULL);
1530                                 }
1531                                 seen_equality = 1;
1532                                 at->at_equality_oid = parse_woid(&ss,code);
1533                                 if ( !at->at_equality_oid ) {
1534                                         *errp = ss;
1535                                         ldap_attributetype_free(at);
1536                                         return NULL;
1537                                 }
1538                         } else if ( !strcmp(sval,"ORDERING") ) {
1539                                 LDAP_FREE(sval);
1540                                 if ( seen_ordering ) {
1541                                         *code = LDAP_SCHERR_DUPOPT;
1542                                         *errp = ss;
1543                                         ldap_attributetype_free(at);
1544                                         return(NULL);
1545                                 }
1546                                 seen_ordering = 1;
1547                                 at->at_ordering_oid = parse_woid(&ss,code);
1548                                 if ( !at->at_ordering_oid ) {
1549                                         *errp = ss;
1550                                         ldap_attributetype_free(at);
1551                                         return NULL;
1552                                 }
1553                         } else if ( !strcmp(sval,"SUBSTR") ) {
1554                                 LDAP_FREE(sval);
1555                                 if ( seen_substr ) {
1556                                         *code = LDAP_SCHERR_DUPOPT;
1557                                         *errp = ss;
1558                                         ldap_attributetype_free(at);
1559                                         return(NULL);
1560                                 }
1561                                 seen_substr = 1;
1562                                 at->at_substr_oid = parse_woid(&ss,code);
1563                                 if ( !at->at_substr_oid ) {
1564                                         *errp = ss;
1565                                         ldap_attributetype_free(at);
1566                                         return NULL;
1567                                 }
1568                         } else if ( !strcmp(sval,"SYNTAX") ) {
1569                                 LDAP_FREE(sval);
1570                                 if ( seen_syntax ) {
1571                                         *code = LDAP_SCHERR_DUPOPT;
1572                                         *errp = ss;
1573                                         ldap_attributetype_free(at);
1574                                         return(NULL);
1575                                 }
1576                                 seen_syntax = 1;
1577                                 parse_whsp(&ss);
1578                                 at->at_syntax_oid =
1579                                         parse_noidlen(&ss,
1580                                                       code,
1581                                                       &at->at_syntax_len,
1582                                                       flags);
1583                                 if ( !at->at_syntax_oid ) {
1584                                         *errp = ss;
1585                                         ldap_attributetype_free(at);
1586                                         return NULL;
1587                                 }
1588                                 parse_whsp(&ss);
1589                         } else if ( !strcmp(sval,"SINGLE-VALUE") ) {
1590                                 LDAP_FREE(sval);
1591                                 if ( at->at_single_value ) {
1592                                         *code = LDAP_SCHERR_DUPOPT;
1593                                         *errp = ss;
1594                                         ldap_attributetype_free(at);
1595                                         return(NULL);
1596                                 }
1597                                 at->at_single_value = LDAP_SCHEMA_YES;
1598                                 parse_whsp(&ss);
1599                         } else if ( !strcmp(sval,"COLLECTIVE") ) {
1600                                 LDAP_FREE(sval);
1601                                 if ( at->at_collective ) {
1602                                         *code = LDAP_SCHERR_DUPOPT;
1603                                         *errp = ss;
1604                                         ldap_attributetype_free(at);
1605                                         return(NULL);
1606                                 }
1607                                 at->at_collective = LDAP_SCHEMA_YES;
1608                                 parse_whsp(&ss);
1609                         } else if ( !strcmp(sval,"NO-USER-MODIFICATION") ) {
1610                                 LDAP_FREE(sval);
1611                                 if ( at->at_no_user_mod ) {
1612                                         *code = LDAP_SCHERR_DUPOPT;
1613                                         *errp = ss;
1614                                         ldap_attributetype_free(at);
1615                                         return(NULL);
1616                                 }
1617                                 at->at_no_user_mod = LDAP_SCHEMA_YES;
1618                                 parse_whsp(&ss);
1619                         } else if ( !strcmp(sval,"USAGE") ) {
1620                                 LDAP_FREE(sval);
1621                                 if ( seen_usage ) {
1622                                         *code = LDAP_SCHERR_DUPOPT;
1623                                         *errp = ss;
1624                                         ldap_attributetype_free(at);
1625                                         return(NULL);
1626                                 }
1627                                 seen_usage = 1;
1628                                 parse_whsp(&ss);
1629                                 kind = get_token(&ss,&sval);
1630                                 if ( kind != TK_BAREWORD ) {
1631                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1632                                         *errp = ss;
1633                                         LDAP_FREE(sval);
1634                                         ldap_attributetype_free(at);
1635                                         return NULL;
1636                                 }
1637                                 if ( !strcasecmp(sval,"userApplications") )
1638                                         at->at_usage =
1639                                             LDAP_SCHEMA_USER_APPLICATIONS;
1640                                 else if ( !strcasecmp(sval,"directoryOperation") )
1641                                         at->at_usage =
1642                                             LDAP_SCHEMA_DIRECTORY_OPERATION;
1643                                 else if ( !strcasecmp(sval,"distributedOperation") )
1644                                         at->at_usage =
1645                                             LDAP_SCHEMA_DISTRIBUTED_OPERATION;
1646                                 else if ( !strcasecmp(sval,"dSAOperation") )
1647                                         at->at_usage =
1648                                             LDAP_SCHEMA_DSA_OPERATION;
1649                                 else {
1650                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1651                                         *errp = ss;
1652                                         LDAP_FREE(sval);
1653                                         ldap_attributetype_free(at);
1654                                         return NULL;
1655                                 }
1656                                 LDAP_FREE(sval);
1657                                 parse_whsp(&ss);
1658                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1659                                 /* Should be parse_qdstrings */
1660                                 ext_vals = parse_qdescrs(&ss, code);
1661                                 if ( !ext_vals ) {
1662                                         *errp = ss;
1663                                         ldap_attributetype_free(at);
1664                                         return NULL;
1665                                 }
1666                                 if ( add_extension(&at->at_extensions,
1667                                                     sval, ext_vals) ) {
1668                                         *code = LDAP_SCHERR_OUTOFMEM;
1669                                         *errp = ss;
1670                                         LDAP_FREE(sval);
1671                                         ldap_attributetype_free(at);
1672                                         return NULL;
1673                                 }
1674                         } else {
1675                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1676                                 *errp = ss;
1677                                 LDAP_FREE(sval);
1678                                 ldap_attributetype_free(at);
1679                                 return NULL;
1680                         }
1681                         break;
1682                 default:
1683                         *code = LDAP_SCHERR_UNEXPTOKEN;
1684                         *errp = ss;
1685                         LDAP_FREE(sval);
1686                         ldap_attributetype_free(at);
1687                         return NULL;
1688                 }
1689         }
1690 }
1691
1692 void
1693 ldap_objectclass_free(LDAP_OBJECT_CLASS * oc)
1694 {
1695         LDAP_FREE(oc->oc_oid);
1696         LDAP_VFREE(oc->oc_names);
1697         LDAP_FREE(oc->oc_desc);
1698         LDAP_VFREE(oc->oc_sup_oids);
1699         LDAP_VFREE(oc->oc_at_oids_must);
1700         LDAP_VFREE(oc->oc_at_oids_may);
1701         free_extensions(oc->oc_extensions);
1702         LDAP_FREE(oc);
1703 }
1704
1705 LDAP_OBJECT_CLASS *
1706 ldap_str2objectclass( const char * s, int * code, const char ** errp, const int flags )
1707 {
1708         int kind;
1709         const char * ss = s;
1710         char * sval;
1711         int seen_name = 0;
1712         int seen_desc = 0;
1713         int seen_obsolete = 0;
1714         int seen_sup = 0;
1715         int seen_kind = 0;
1716         int seen_must = 0;
1717         int seen_may = 0;
1718         LDAP_OBJECT_CLASS * oc;
1719         char ** ext_vals;
1720         const char * savepos;
1721
1722         if ( !s ) {
1723                 *code = LDAP_SCHERR_EMPTY;
1724                 *errp = "";
1725                 return NULL;
1726         }
1727
1728         *errp = s;
1729         oc = LDAP_CALLOC(1,sizeof(LDAP_OBJECT_CLASS));
1730
1731         if ( !oc ) {
1732                 *code = LDAP_SCHERR_OUTOFMEM;
1733                 return NULL;
1734         }
1735
1736         kind = get_token(&ss,&sval);
1737         if ( kind != TK_LEFTPAREN ) {
1738                 *code = LDAP_SCHERR_NOLEFTPAREN;
1739                 LDAP_FREE(sval);
1740                 ldap_objectclass_free(oc);
1741                 return NULL;
1742         }
1743
1744         /*
1745          * Definitions MUST begin with an OID in the numericoid format.
1746          * However, this routine is used by clients to parse the response
1747          * from servers and very well known servers will provide an OID
1748          * in the wrong format or even no OID at all.  We do our best to
1749          * extract info from those servers.
1750          */
1751         parse_whsp(&ss);
1752         savepos = ss;
1753         oc->oc_oid = parse_numericoid(&ss,code,0);
1754         if ( !oc->oc_oid ) {
1755                 if ( flags & LDAP_SCHEMA_ALLOW_ALL ) {
1756                         /* Backtracking */
1757                         ss = savepos;
1758                         kind = get_token(&ss,&sval);
1759                         if ( kind == TK_BAREWORD ) {
1760                                 if ( !strcmp(sval, "NAME") ||
1761                                      !strcmp(sval, "DESC") ||
1762                                      !strcmp(sval, "OBSOLETE") ||
1763                                      !strcmp(sval, "SUP") ||
1764                                      !strcmp(sval, "ABSTRACT") ||
1765                                      !strcmp(sval, "STRUCTURAL") ||
1766                                      !strcmp(sval, "AUXILIARY") ||
1767                                      !strcmp(sval, "MUST") ||
1768                                      !strncmp(sval, "X-", 2) ) {
1769                                         /* Missing OID, backtrack */
1770                                         ss = savepos;
1771                                 } else {
1772                                         /* Non-numerical OID, ignore */
1773                                 }
1774                         }
1775                         LDAP_FREE(sval);
1776                 } else {
1777                         *errp = ss;
1778                         ldap_objectclass_free(oc);
1779                         return NULL;
1780                 }
1781         }
1782         parse_whsp(&ss);
1783
1784         /*
1785          * Beyond this point we will be liberal an accept the items
1786          * in any order.
1787          */
1788         while (1) {
1789                 kind = get_token(&ss,&sval);
1790                 switch (kind) {
1791                 case TK_EOS:
1792                         *code = LDAP_SCHERR_NORIGHTPAREN;
1793                         *errp = ss;
1794                         ldap_objectclass_free(oc);
1795                         return NULL;
1796                 case TK_RIGHTPAREN:
1797                         return oc;
1798                 case TK_BAREWORD:
1799                         if ( !strcmp(sval,"NAME") ) {
1800                                 LDAP_FREE(sval);
1801                                 if ( seen_name ) {
1802                                         *code = LDAP_SCHERR_DUPOPT;
1803                                         *errp = ss;
1804                                         ldap_objectclass_free(oc);
1805                                         return(NULL);
1806                                 }
1807                                 seen_name = 1;
1808                                 oc->oc_names = parse_qdescrs(&ss,code);
1809                                 if ( !oc->oc_names ) {
1810                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1811                                                 *code = LDAP_SCHERR_BADNAME;
1812                                         *errp = ss;
1813                                         ldap_objectclass_free(oc);
1814                                         return NULL;
1815                                 }
1816                         } else if ( !strcmp(sval,"DESC") ) {
1817                                 LDAP_FREE(sval);
1818                                 if ( seen_desc ) {
1819                                         *code = LDAP_SCHERR_DUPOPT;
1820                                         *errp = ss;
1821                                         ldap_objectclass_free(oc);
1822                                         return(NULL);
1823                                 }
1824                                 seen_desc = 1;
1825                                 parse_whsp(&ss);
1826                                 kind = get_token(&ss,&sval);
1827                                 if ( kind != TK_QDSTRING ) {
1828                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1829                                         *errp = ss;
1830                                         LDAP_FREE(sval);
1831                                         ldap_objectclass_free(oc);
1832                                         return NULL;
1833                                 }
1834                                 oc->oc_desc = sval;
1835                                 parse_whsp(&ss);
1836                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1837                                 LDAP_FREE(sval);
1838                                 if ( seen_obsolete ) {
1839                                         *code = LDAP_SCHERR_DUPOPT;
1840                                         *errp = ss;
1841                                         ldap_objectclass_free(oc);
1842                                         return(NULL);
1843                                 }
1844                                 seen_obsolete = 1;
1845                                 oc->oc_obsolete = LDAP_SCHEMA_YES;
1846                                 parse_whsp(&ss);
1847                         } else if ( !strcmp(sval,"SUP") ) {
1848                                 LDAP_FREE(sval);
1849                                 if ( seen_sup ) {
1850                                         *code = LDAP_SCHERR_DUPOPT;
1851                                         *errp = ss;
1852                                         ldap_objectclass_free(oc);
1853                                         return(NULL);
1854                                 }
1855                                 seen_sup = 1;
1856                                 oc->oc_sup_oids = parse_oids(&ss,
1857                                                              code,
1858                                                              flags);
1859                                 if ( !oc->oc_sup_oids ) {
1860                                         *errp = ss;
1861                                         ldap_objectclass_free(oc);
1862                                         return NULL;
1863                                 }
1864                         } else if ( !strcmp(sval,"ABSTRACT") ) {
1865                                 LDAP_FREE(sval);
1866                                 if ( seen_kind ) {
1867                                         *code = LDAP_SCHERR_DUPOPT;
1868                                         *errp = ss;
1869                                         ldap_objectclass_free(oc);
1870                                         return(NULL);
1871                                 }
1872                                 seen_kind = 1;
1873                                 oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
1874                                 parse_whsp(&ss);
1875                         } else if ( !strcmp(sval,"STRUCTURAL") ) {
1876                                 LDAP_FREE(sval);
1877                                 if ( seen_kind ) {
1878                                         *code = LDAP_SCHERR_DUPOPT;
1879                                         *errp = ss;
1880                                         ldap_objectclass_free(oc);
1881                                         return(NULL);
1882                                 }
1883                                 seen_kind = 1;
1884                                 oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
1885                                 parse_whsp(&ss);
1886                         } else if ( !strcmp(sval,"AUXILIARY") ) {
1887                                 LDAP_FREE(sval);
1888                                 if ( seen_kind ) {
1889                                         *code = LDAP_SCHERR_DUPOPT;
1890                                         *errp = ss;
1891                                         ldap_objectclass_free(oc);
1892                                         return(NULL);
1893                                 }
1894                                 seen_kind = 1;
1895                                 oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
1896                                 parse_whsp(&ss);
1897                         } else if ( !strcmp(sval,"MUST") ) {
1898                                 LDAP_FREE(sval);
1899                                 if ( seen_must ) {
1900                                         *code = LDAP_SCHERR_DUPOPT;
1901                                         *errp = ss;
1902                                         ldap_objectclass_free(oc);
1903                                         return(NULL);
1904                                 }
1905                                 seen_must = 1;
1906                                 oc->oc_at_oids_must = parse_oids(&ss,code,0);
1907                                 if ( !oc->oc_at_oids_must ) {
1908                                         *errp = ss;
1909                                         ldap_objectclass_free(oc);
1910                                         return NULL;
1911                                 }
1912                                 parse_whsp(&ss);
1913                         } else if ( !strcmp(sval,"MAY") ) {
1914                                 LDAP_FREE(sval);
1915                                 if ( seen_may ) {
1916                                         *code = LDAP_SCHERR_DUPOPT;
1917                                         *errp = ss;
1918                                         ldap_objectclass_free(oc);
1919                                         return(NULL);
1920                                 }
1921                                 seen_may = 1;
1922                                 oc->oc_at_oids_may = parse_oids(&ss,code,0);
1923                                 if ( !oc->oc_at_oids_may ) {
1924                                         *errp = ss;
1925                                         ldap_objectclass_free(oc);
1926                                         return NULL;
1927                                 }
1928                                 parse_whsp(&ss);
1929                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1930                                 /* Should be parse_qdstrings */
1931                                 ext_vals = parse_qdescrs(&ss, code);
1932                                 if ( !ext_vals ) {
1933                                         *errp = ss;
1934                                         ldap_objectclass_free(oc);
1935                                         return NULL;
1936                                 }
1937                                 if ( add_extension(&oc->oc_extensions,
1938                                                     sval, ext_vals) ) {
1939                                         *code = LDAP_SCHERR_OUTOFMEM;
1940                                         *errp = ss;
1941                                         LDAP_FREE(sval);
1942                                         ldap_objectclass_free(oc);
1943                                         return NULL;
1944                                 }
1945                         } else {
1946                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1947                                 *errp = ss;
1948                                 LDAP_FREE(sval);
1949                                 ldap_objectclass_free(oc);
1950                                 return NULL;
1951                         }
1952                         break;
1953                 default:
1954                         *code = LDAP_SCHERR_UNEXPTOKEN;
1955                         *errp = ss;
1956                         LDAP_FREE(sval);
1957                         ldap_objectclass_free(oc);
1958                         return NULL;
1959                 }
1960         }
1961 }
1962
1963 static char *const err2text[] = {
1964         "Success",
1965         "Out of memory",
1966         "Unexpected token",
1967         "Missing opening parenthesis",
1968         "Missing closing parenthesis",
1969         "Expecting digit",
1970         "Expecting a name",
1971         "Bad description",
1972         "Bad superiors",
1973         "Duplicate option",
1974         "Unexpected end of data"
1975 };
1976
1977 char *
1978 ldap_scherr2str(int code)
1979 {
1980         if ( code < 0 || code >= (sizeof(err2text)/sizeof(char *)) ) {
1981                 return "Unknown error";
1982         } else {
1983                 return err2text[code];
1984         }
1985 }