]> git.sur5r.net Git - openldap/blob - libraries/libldap/schema.c
711f6eac563008f24361adcf8497cfa5f0c68d99
[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
25 static LDAP_CONST char *
26 choose_name( char *names[], LDAP_CONST char *fallback )
27 {
28         return( (names != NULL && names[0] != NULL) ? names[0] : fallback );
29 }
30
31 LDAP_CONST char *
32 ldap_syntax2name( LDAP_SYNTAX * syn )
33 {
34         return( syn->syn_oid );
35 }
36
37 LDAP_CONST char *
38 ldap_matchingrule2name( LDAP_MATCHING_RULE * mr )
39 {
40         return( choose_name( mr->mr_names, mr->mr_oid ) );
41 }
42
43 LDAP_CONST char *
44 ldap_attributetype2name( LDAP_ATTRIBUTE_TYPE * at )
45 {
46         return( choose_name( at->at_names, at->at_oid ) );
47 }
48
49 LDAP_CONST char *
50 ldap_objectclass2name( LDAP_OBJECT_CLASS * oc )
51 {
52         return( choose_name( oc->oc_names, oc->oc_oid ) );
53 }
54
55
56 /*
57  * When pretty printing the entities we will be appending to a buffer.
58  * Since checking for overflow, realloc'ing and checking if no error
59  * is extremely boring, we will use a protection layer that will let
60  * us blissfully ignore the error until the end.  This layer is
61  * implemented with the help of the next type.
62  */
63
64 typedef struct safe_string {
65         char * val;
66         ber_len_t size;
67         ber_len_t pos;
68         int at_whsp;
69 } safe_string;
70
71 static safe_string *
72 new_safe_string(int size)
73 {
74         safe_string * ss;
75         
76         ss = LDAP_MALLOC(sizeof(safe_string));
77         if ( !ss )
78                 return(NULL);
79
80         ss->val = LDAP_MALLOC(size);
81         if ( !ss->val ) {
82                 LDAP_FREE(ss);
83                 return(NULL);
84         }
85
86         ss->size = size;
87         ss->pos = 0;
88         ss->at_whsp = 0;
89
90         return ss;
91 }
92
93 static void
94 safe_string_free(safe_string * ss)
95 {
96         if ( !ss )
97                 return;
98         LDAP_FREE(ss->val);
99         LDAP_FREE(ss);
100 }
101
102 static char *
103 safe_string_val(safe_string * ss)
104 {
105         ss->val[ss->pos] = '\0';
106         return(ss->val);
107 }
108
109 static int
110 append_to_safe_string(safe_string * ss, char * s)
111 {
112         int l = strlen(s);
113         char * temp;
114
115         /*
116          * Some runaway process is trying to append to a string that
117          * overflowed and we could not extend.
118          */
119         if ( !ss->val )
120                 return -1;
121
122         /* We always make sure there is at least one position available */
123         if ( ss->pos + l >= ss->size-1 ) {
124                 ss->size *= 2;
125                 temp = LDAP_REALLOC(ss->val, ss->size);
126                 if ( !temp ) {
127                         /* Trouble, out of memory */
128                         LDAP_FREE(ss->val);
129                         return -1;
130                 }
131                 ss->val = temp;
132         }
133         strncpy(&ss->val[ss->pos], s, l);
134         ss->pos += l;
135         if ( ss->pos > 0 && isspace(ss->val[ss->pos-1]) )
136                 ss->at_whsp = 1;
137         else
138                 ss->at_whsp = 0;
139
140         return 0;
141 }
142
143 static int
144 print_literal(safe_string *ss, char *s)
145 {
146         return(append_to_safe_string(ss,s));
147 }
148
149 static int
150 print_whsp(safe_string *ss)
151 {
152         if ( ss->at_whsp )
153                 return(append_to_safe_string(ss,""));
154         else
155                 return(append_to_safe_string(ss," "));
156 }
157
158 static int
159 print_numericoid(safe_string *ss, char *s)
160 {
161         if ( s )
162                 return(append_to_safe_string(ss,s));
163         else
164                 return(append_to_safe_string(ss,""));
165 }
166
167 /* This one is identical to print_qdescr */
168 static int
169 print_qdstring(safe_string *ss, char *s)
170 {
171         print_whsp(ss);
172         print_literal(ss,"'");
173         append_to_safe_string(ss,s);
174         print_literal(ss,"'");
175         return(print_whsp(ss));
176 }
177
178 static int
179 print_qdescr(safe_string *ss, char *s)
180 {
181         print_whsp(ss);
182         print_literal(ss,"'");
183         append_to_safe_string(ss,s);
184         print_literal(ss,"'");
185         return(print_whsp(ss));
186 }
187
188 static int
189 print_qdescrlist(safe_string *ss, char **sa)
190 {
191         char **sp;
192         int ret = 0;
193         
194         for (sp=sa; *sp; sp++) {
195                 ret = print_qdescr(ss,*sp);
196         }
197         /* If the list was empty, we return zero that is potentially
198          * incorrect, but since we will be still appending things, the
199          * overflow will be detected later.  Maybe FIX.
200          */
201         return(ret);
202 }
203
204 static int
205 print_qdescrs(safe_string *ss, char **sa)
206 {
207         /* The only way to represent an empty list is as a qdescrlist
208          * so, if the list is empty we treat it as a long list.
209          * Really, this is what the syntax mandates.  We should not
210          * be here if the list was empty, but if it happens, a label
211          * has already been output and we cannot undo it.
212          */
213         if ( !sa[0] || ( sa[0] && sa[1] ) ) {
214                 print_whsp(ss);
215                 print_literal(ss,"("/*)*/);
216                 print_qdescrlist(ss,sa);
217                 print_literal(ss,/*(*/")");
218                 return(print_whsp(ss));
219         } else {
220           return(print_qdescr(ss,*sa));
221         }
222 }
223
224 static int
225 print_woid(safe_string *ss, char *s)
226 {
227         print_whsp(ss);
228         append_to_safe_string(ss,s);
229         return print_whsp(ss);
230 }
231
232 static int
233 print_oidlist(safe_string *ss, char **sa)
234 {
235         char **sp;
236
237         for (sp=sa; *(sp+1); sp++) {
238                 print_woid(ss,*sp);
239                 print_literal(ss,"$");
240         }
241         return(print_woid(ss,*sp));
242 }
243
244 static int
245 print_oids(safe_string *ss, char **sa)
246 {
247         if ( sa[0] && sa[1] ) {
248                 print_literal(ss,"("/*)*/);
249                 print_oidlist(ss,sa);
250                 print_whsp(ss);
251                 return(print_literal(ss,/*(*/")"));
252         } else {
253                 return(print_woid(ss,*sa));
254         }
255 }
256
257 static int
258 print_noidlen(safe_string *ss, char *s, int l)
259 {
260         char buf[64];
261         int ret;
262
263         ret = print_numericoid(ss,s);
264         if ( l ) {
265                 sprintf(buf,"{%d}",l);
266                 ret = print_literal(ss,buf);
267         }
268         return(ret);
269 }
270
271 static int
272 print_extensions(safe_string *ss, LDAP_SCHEMA_EXTENSION_ITEM **extensions)
273 {
274         LDAP_SCHEMA_EXTENSION_ITEM **ext;
275
276         if ( extensions ) {
277                 print_whsp(ss);
278                 for ( ext = extensions; *ext != NULL; ext++ ) {
279                         print_literal(ss, (*ext)->lsei_name);
280                         print_whsp(ss);
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 allow_quoted)
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 ( 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 ( 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 )
1028 {
1029         int kind;
1030         const char * ss = s;
1031         char * sval;
1032         int seen_desc = 0;
1033         LDAP_SYNTAX * syn;
1034         char ** ssdummy;
1035
1036         if ( !s ) {
1037                 *code = LDAP_SCHERR_EMPTY;
1038                 *errp = "";
1039                 return NULL;
1040         }
1041
1042         *errp = s;
1043         syn = LDAP_CALLOC(1,sizeof(LDAP_SYNTAX));
1044
1045         if ( !syn ) {
1046                 *code = LDAP_SCHERR_OUTOFMEM;
1047                 return NULL;
1048         }
1049
1050         kind = get_token(&ss,&sval);
1051         if ( kind != TK_LEFTPAREN ) {
1052                 LDAP_FREE(sval);
1053                 *code = LDAP_SCHERR_NOLEFTPAREN;
1054                 ldap_syntax_free(syn);
1055                 return NULL;
1056         }
1057
1058         parse_whsp(&ss);
1059         syn->syn_oid = parse_numericoid(&ss,code,0);
1060         if ( !syn->syn_oid ) {
1061                 *errp = ss;
1062                 ldap_syntax_free(syn);
1063                 return NULL;
1064         }
1065         parse_whsp(&ss);
1066
1067         /*
1068          * Beyond this point we will be liberal and accept the items
1069          * in any order.
1070          */
1071         while (1) {
1072                 kind = get_token(&ss,&sval);
1073                 switch (kind) {
1074                 case TK_EOS:
1075                         *code = LDAP_SCHERR_NORIGHTPAREN;
1076                         *errp = ss;
1077                         ldap_syntax_free(syn);
1078                         return NULL;
1079                 case TK_RIGHTPAREN:
1080                         return syn;
1081                 case TK_BAREWORD:
1082                         if ( !strcmp(sval,"DESC") ) {
1083                                 LDAP_FREE(sval);
1084                                 if ( seen_desc ) {
1085                                         *code = LDAP_SCHERR_DUPOPT;
1086                                         *errp = ss;
1087                                         ldap_syntax_free(syn);
1088                                         return(NULL);
1089                                 }
1090                                 seen_desc = 1;
1091                                 parse_whsp(&ss);
1092                                 kind = get_token(&ss,&sval);
1093                                 if ( kind != TK_QDSTRING ) {
1094                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1095                                         *errp = ss;
1096                                         LDAP_FREE(sval);
1097                                         ldap_syntax_free(syn);
1098                                         return NULL;
1099                                 }
1100                                 syn->syn_desc = sval;
1101                                 parse_whsp(&ss);
1102                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1103                                 LDAP_FREE(sval);
1104                                 /* Should be parse_qdstrings */
1105                                 ssdummy = parse_qdescrs(&ss, code);
1106                                 if ( !ssdummy ) {
1107                                         *errp = ss;
1108                                         ldap_syntax_free(syn);
1109                                         return NULL;
1110                                 }
1111                         } else {
1112                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1113                                 *errp = ss;
1114                                 LDAP_FREE(sval);
1115                                 ldap_syntax_free(syn);
1116                                 return NULL;
1117                         }
1118                         break;
1119                 default:
1120                         *code = LDAP_SCHERR_UNEXPTOKEN;
1121                         *errp = ss;
1122                         LDAP_FREE(sval);
1123                         ldap_syntax_free(syn);
1124                         return NULL;
1125                 }
1126         }
1127 }
1128
1129 void
1130 ldap_matchingrule_free( LDAP_MATCHING_RULE * mr )
1131 {
1132         LDAP_FREE(mr->mr_oid);
1133         LDAP_VFREE(mr->mr_names);
1134         LDAP_FREE(mr->mr_desc);
1135         LDAP_FREE(mr->mr_syntax_oid);
1136         free_extensions(mr->mr_extensions);
1137         LDAP_FREE(mr);
1138 }
1139
1140 LDAP_MATCHING_RULE *
1141 ldap_str2matchingrule( const char * s, int * code, const char ** errp )
1142 {
1143         int kind;
1144         const char * ss = s;
1145         char * sval;
1146         int be_liberal = 1;     /* Future additional argument */
1147         int seen_name = 0;
1148         int seen_desc = 0;
1149         int seen_obsolete = 0;
1150         int seen_syntax = 0;
1151         LDAP_MATCHING_RULE * mr;
1152         char ** ssdummy;
1153         const char * savepos;
1154
1155         if ( !s ) {
1156                 *code = LDAP_SCHERR_EMPTY;
1157                 *errp = "";
1158                 return NULL;
1159         }
1160
1161         *errp = s;
1162         mr = LDAP_CALLOC(1,sizeof(LDAP_MATCHING_RULE));
1163
1164         if ( !mr ) {
1165                 *code = LDAP_SCHERR_OUTOFMEM;
1166                 return NULL;
1167         }
1168
1169         kind = get_token(&ss,&sval);
1170         if ( kind != TK_LEFTPAREN ) {
1171                 *code = LDAP_SCHERR_NOLEFTPAREN;
1172                 LDAP_FREE(sval);
1173                 ldap_matchingrule_free(mr);
1174                 return NULL;
1175         }
1176
1177         parse_whsp(&ss);
1178         savepos = ss;
1179         mr->mr_oid = parse_numericoid(&ss,code,be_liberal);
1180         if ( !mr->mr_oid ) {
1181                 if ( be_liberal ) {
1182                         /* Backtracking */
1183                         ss = savepos;
1184                         kind = get_token(&ss,&sval);
1185                         if ( kind == TK_BAREWORD ) {
1186                                 if ( !strcmp(sval, "NAME") ||
1187                                      !strcmp(sval, "DESC") ||
1188                                      !strcmp(sval, "OBSOLETE") ||
1189                                      !strcmp(sval, "SYNTAX") ||
1190                                      !strncmp(sval, "X-", 2) ) {
1191                                         /* Missing OID, backtrack */
1192                                         ss = savepos;
1193                                 } else {
1194                                         /* Non-numerical OID, ignore */
1195                                 }
1196                         }
1197                         LDAP_FREE(sval);
1198                 } else {
1199                         *errp = ss;
1200                         ldap_matchingrule_free(mr);
1201                         return NULL;
1202                 }
1203         }
1204         parse_whsp(&ss);
1205
1206         /*
1207          * Beyond this point we will be liberal and accept the items
1208          * in any order.
1209          */
1210         while (1) {
1211                 kind = get_token(&ss,&sval);
1212                 switch (kind) {
1213                 case TK_EOS:
1214                         *code = LDAP_SCHERR_NORIGHTPAREN;
1215                         *errp = ss;
1216                         ldap_matchingrule_free(mr);
1217                         return NULL;
1218                 case TK_RIGHTPAREN:
1219                         return mr;
1220                 case TK_BAREWORD:
1221                         if ( !strcmp(sval,"NAME") ) {
1222                                 LDAP_FREE(sval);
1223                                 if ( seen_name ) {
1224                                         *code = LDAP_SCHERR_DUPOPT;
1225                                         *errp = ss;
1226                                         ldap_matchingrule_free(mr);
1227                                         return(NULL);
1228                                 }
1229                                 seen_name = 1;
1230                                 mr->mr_names = parse_qdescrs(&ss,code);
1231                                 if ( !mr->mr_names ) {
1232                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1233                                                 *code = LDAP_SCHERR_BADNAME;
1234                                         *errp = ss;
1235                                         ldap_matchingrule_free(mr);
1236                                         return NULL;
1237                                 }
1238                         } else if ( !strcmp(sval,"DESC") ) {
1239                                 LDAP_FREE(sval);
1240                                 if ( seen_desc ) {
1241                                         *code = LDAP_SCHERR_DUPOPT;
1242                                         *errp = ss;
1243                                         ldap_matchingrule_free(mr);
1244                                         return(NULL);
1245                                 }
1246                                 seen_desc = 1;
1247                                 parse_whsp(&ss);
1248                                 kind = get_token(&ss,&sval);
1249                                 if ( kind != TK_QDSTRING ) {
1250                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1251                                         *errp = ss;
1252                                         LDAP_FREE(sval);
1253                                         ldap_matchingrule_free(mr);
1254                                         return NULL;
1255                                 }
1256                                 mr->mr_desc = sval;
1257                                 parse_whsp(&ss);
1258                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1259                                 LDAP_FREE(sval);
1260                                 if ( seen_obsolete ) {
1261                                         *code = LDAP_SCHERR_DUPOPT;
1262                                         *errp = ss;
1263                                         ldap_matchingrule_free(mr);
1264                                         return(NULL);
1265                                 }
1266                                 seen_obsolete = 1;
1267                                 mr->mr_obsolete = LDAP_SCHEMA_YES;
1268                                 parse_whsp(&ss);
1269                         } else if ( !strcmp(sval,"SYNTAX") ) {
1270                                 LDAP_FREE(sval);
1271                                 if ( seen_syntax ) {
1272                                         *code = LDAP_SCHERR_DUPOPT;
1273                                         *errp = ss;
1274                                         ldap_matchingrule_free(mr);
1275                                         return(NULL);
1276                                 }
1277                                 seen_syntax = 1;
1278                                 parse_whsp(&ss);
1279                                 mr->mr_syntax_oid =
1280                                         parse_numericoid(&ss,code,be_liberal);
1281                                 if ( !mr->mr_syntax_oid ) {
1282                                         *errp = ss;
1283                                         ldap_matchingrule_free(mr);
1284                                         return NULL;
1285                                 }
1286                                 parse_whsp(&ss);
1287                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1288                                 LDAP_FREE(sval);
1289                                 /* Should be parse_qdstrings */
1290                                 ssdummy = parse_qdescrs(&ss, code);
1291                                 if ( !ssdummy ) {
1292                                         *errp = ss;
1293                                         ldap_matchingrule_free(mr);
1294                                         return NULL;
1295                                 }
1296                         } else {
1297                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1298                                 *errp = ss;
1299                                 LDAP_FREE(sval);
1300                                 ldap_matchingrule_free(mr);
1301                                 return NULL;
1302                         }
1303                         break;
1304                 default:
1305                         *code = LDAP_SCHERR_UNEXPTOKEN;
1306                         *errp = ss;
1307                         LDAP_FREE(sval);
1308                         ldap_matchingrule_free(mr);
1309                         return NULL;
1310                 }
1311         }
1312 }
1313
1314 void
1315 ldap_attributetype_free(LDAP_ATTRIBUTE_TYPE * at)
1316 {
1317         LDAP_FREE(at->at_oid);
1318         LDAP_VFREE(at->at_names);
1319         LDAP_FREE(at->at_desc);
1320         LDAP_FREE(at->at_sup_oid);
1321         LDAP_FREE(at->at_equality_oid);
1322         LDAP_FREE(at->at_ordering_oid);
1323         LDAP_FREE(at->at_substr_oid);
1324         LDAP_FREE(at->at_syntax_oid);
1325         free_extensions(at->at_extensions);
1326         LDAP_FREE(at);
1327 }
1328
1329 LDAP_ATTRIBUTE_TYPE *
1330 ldap_str2attributetype( const char * s, int * code, const char ** errp )
1331 {
1332         int kind;
1333         const char * ss = s;
1334         char * sval;
1335         int be_liberal = 1;     /* Future additional argument */
1336         int seen_name = 0;
1337         int seen_desc = 0;
1338         int seen_obsolete = 0;
1339         int seen_sup = 0;
1340         int seen_equality = 0;
1341         int seen_ordering = 0;
1342         int seen_substr = 0;
1343         int seen_syntax = 0;
1344         int seen_usage = 0;
1345         LDAP_ATTRIBUTE_TYPE * at;
1346         char ** ext_vals;
1347         const char * savepos;
1348
1349         if ( !s ) {
1350                 *code = LDAP_SCHERR_EMPTY;
1351                 *errp = "";
1352                 return NULL;
1353         }
1354
1355         *errp = s;
1356         at = LDAP_CALLOC(1,sizeof(LDAP_ATTRIBUTE_TYPE));
1357
1358         if ( !at ) {
1359                 *code = LDAP_SCHERR_OUTOFMEM;
1360                 return NULL;
1361         }
1362
1363         kind = get_token(&ss,&sval);
1364         if ( kind != TK_LEFTPAREN ) {
1365                 *code = LDAP_SCHERR_NOLEFTPAREN;
1366                 LDAP_FREE(sval);
1367                 ldap_attributetype_free(at);
1368                 return NULL;
1369         }
1370
1371         /*
1372          * Definitions MUST begin with an OID in the numericoid format.
1373          * However, this routine is used by clients to parse the response
1374          * from servers and very well known servers will provide an OID
1375          * in the wrong format or even no OID at all.  We do our best to
1376          * extract info from those servers.
1377          */
1378         parse_whsp(&ss);
1379         savepos = ss;
1380         at->at_oid = parse_numericoid(&ss,code,0);
1381         if ( !at->at_oid ) {
1382                 if ( be_liberal ) {
1383                         /* Backtracking */
1384                         ss = savepos;
1385                         kind = get_token(&ss,&sval);
1386                         if ( kind == TK_BAREWORD ) {
1387                                 if ( !strcmp(sval, "NAME") ||
1388                                      !strcmp(sval, "DESC") ||
1389                                      !strcmp(sval, "OBSOLETE") ||
1390                                      !strcmp(sval, "SUP") ||
1391                                      !strcmp(sval, "EQUALITY") ||
1392                                      !strcmp(sval, "ORDERING") ||
1393                                      !strcmp(sval, "SUBSTR") ||
1394                                      !strcmp(sval, "SYNTAX") ||
1395                                      !strcmp(sval, "SINGLE-VALUE") ||
1396                                      !strcmp(sval, "COLLECTIVE") ||
1397                                      !strcmp(sval, "NO-USER-MODIFICATION") ||
1398                                      !strcmp(sval, "USAGE") ||
1399                                      !strncmp(sval, "X-", 2) ) {
1400                                         /* Missing OID, backtrack */
1401                                         ss = savepos;
1402                                 } else {
1403                                         /* Non-numerical OID, ignore */
1404                                 }
1405                         }
1406                         LDAP_FREE(sval);
1407                 } else {
1408                         *errp = ss;
1409                         ldap_attributetype_free(at);
1410                         return NULL;
1411                 }
1412         }
1413         parse_whsp(&ss);
1414
1415         /*
1416          * Beyond this point we will be liberal and accept the items
1417          * in any order.
1418          */
1419         while (1) {
1420                 kind = get_token(&ss,&sval);
1421                 switch (kind) {
1422                 case TK_EOS:
1423                         *code = LDAP_SCHERR_NORIGHTPAREN;
1424                         *errp = ss;
1425                         ldap_attributetype_free(at);
1426                         return NULL;
1427                 case TK_RIGHTPAREN:
1428                         return at;
1429                 case TK_BAREWORD:
1430                         if ( !strcmp(sval,"NAME") ) {
1431                                 LDAP_FREE(sval);
1432                                 if ( seen_name ) {
1433                                         *code = LDAP_SCHERR_DUPOPT;
1434                                         *errp = ss;
1435                                         ldap_attributetype_free(at);
1436                                         return(NULL);
1437                                 }
1438                                 seen_name = 1;
1439                                 at->at_names = parse_qdescrs(&ss,code);
1440                                 if ( !at->at_names ) {
1441                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1442                                                 *code = LDAP_SCHERR_BADNAME;
1443                                         *errp = ss;
1444                                         ldap_attributetype_free(at);
1445                                         return NULL;
1446                                 }
1447                         } else if ( !strcmp(sval,"DESC") ) {
1448                                 LDAP_FREE(sval);
1449                                 if ( seen_desc ) {
1450                                         *code = LDAP_SCHERR_DUPOPT;
1451                                         *errp = ss;
1452                                         ldap_attributetype_free(at);
1453                                         return(NULL);
1454                                 }
1455                                 seen_desc = 1;
1456                                 parse_whsp(&ss);
1457                                 kind = get_token(&ss,&sval);
1458                                 if ( kind != TK_QDSTRING ) {
1459                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1460                                         *errp = ss;
1461                                         LDAP_FREE(sval);
1462                                         ldap_attributetype_free(at);
1463                                         return NULL;
1464                                 }
1465                                 at->at_desc = sval;
1466                                 parse_whsp(&ss);
1467                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1468                                 LDAP_FREE(sval);
1469                                 if ( seen_obsolete ) {
1470                                         *code = LDAP_SCHERR_DUPOPT;
1471                                         *errp = ss;
1472                                         ldap_attributetype_free(at);
1473                                         return(NULL);
1474                                 }
1475                                 seen_obsolete = 1;
1476                                 at->at_obsolete = LDAP_SCHEMA_YES;
1477                                 parse_whsp(&ss);
1478                         } else if ( !strcmp(sval,"SUP") ) {
1479                                 LDAP_FREE(sval);
1480                                 if ( seen_sup ) {
1481                                         *code = LDAP_SCHERR_DUPOPT;
1482                                         *errp = ss;
1483                                         ldap_attributetype_free(at);
1484                                         return(NULL);
1485                                 }
1486                                 seen_sup = 1;
1487                                 at->at_sup_oid = parse_woid(&ss,code);
1488                                 if ( !at->at_sup_oid ) {
1489                                         *errp = ss;
1490                                         ldap_attributetype_free(at);
1491                                         return NULL;
1492                                 }
1493                         } else if ( !strcmp(sval,"EQUALITY") ) {
1494                                 LDAP_FREE(sval);
1495                                 if ( seen_equality ) {
1496                                         *code = LDAP_SCHERR_DUPOPT;
1497                                         *errp = ss;
1498                                         ldap_attributetype_free(at);
1499                                         return(NULL);
1500                                 }
1501                                 seen_equality = 1;
1502                                 at->at_equality_oid = parse_woid(&ss,code);
1503                                 if ( !at->at_equality_oid ) {
1504                                         *errp = ss;
1505                                         ldap_attributetype_free(at);
1506                                         return NULL;
1507                                 }
1508                         } else if ( !strcmp(sval,"ORDERING") ) {
1509                                 LDAP_FREE(sval);
1510                                 if ( seen_ordering ) {
1511                                         *code = LDAP_SCHERR_DUPOPT;
1512                                         *errp = ss;
1513                                         ldap_attributetype_free(at);
1514                                         return(NULL);
1515                                 }
1516                                 seen_ordering = 1;
1517                                 at->at_ordering_oid = parse_woid(&ss,code);
1518                                 if ( !at->at_ordering_oid ) {
1519                                         *errp = ss;
1520                                         ldap_attributetype_free(at);
1521                                         return NULL;
1522                                 }
1523                         } else if ( !strcmp(sval,"SUBSTR") ) {
1524                                 LDAP_FREE(sval);
1525                                 if ( seen_substr ) {
1526                                         *code = LDAP_SCHERR_DUPOPT;
1527                                         *errp = ss;
1528                                         ldap_attributetype_free(at);
1529                                         return(NULL);
1530                                 }
1531                                 seen_substr = 1;
1532                                 at->at_substr_oid = parse_woid(&ss,code);
1533                                 if ( !at->at_substr_oid ) {
1534                                         *errp = ss;
1535                                         ldap_attributetype_free(at);
1536                                         return NULL;
1537                                 }
1538                         } else if ( !strcmp(sval,"SYNTAX") ) {
1539                                 LDAP_FREE(sval);
1540                                 if ( seen_syntax ) {
1541                                         *code = LDAP_SCHERR_DUPOPT;
1542                                         *errp = ss;
1543                                         ldap_attributetype_free(at);
1544                                         return(NULL);
1545                                 }
1546                                 seen_syntax = 1;
1547                                 parse_whsp(&ss);
1548                                 at->at_syntax_oid =
1549                                         parse_noidlen(&ss,
1550                                                       code,
1551                                                       &at->at_syntax_len,
1552                                                       be_liberal);
1553                                 if ( !at->at_syntax_oid ) {
1554                                         *errp = ss;
1555                                         ldap_attributetype_free(at);
1556                                         return NULL;
1557                                 }
1558                                 parse_whsp(&ss);
1559                         } else if ( !strcmp(sval,"SINGLE-VALUE") ) {
1560                                 LDAP_FREE(sval);
1561                                 if ( at->at_single_value ) {
1562                                         *code = LDAP_SCHERR_DUPOPT;
1563                                         *errp = ss;
1564                                         ldap_attributetype_free(at);
1565                                         return(NULL);
1566                                 }
1567                                 at->at_single_value = LDAP_SCHEMA_YES;
1568                                 parse_whsp(&ss);
1569                         } else if ( !strcmp(sval,"COLLECTIVE") ) {
1570                                 LDAP_FREE(sval);
1571                                 if ( at->at_collective ) {
1572                                         *code = LDAP_SCHERR_DUPOPT;
1573                                         *errp = ss;
1574                                         ldap_attributetype_free(at);
1575                                         return(NULL);
1576                                 }
1577                                 at->at_collective = LDAP_SCHEMA_YES;
1578                                 parse_whsp(&ss);
1579                         } else if ( !strcmp(sval,"NO-USER-MODIFICATION") ) {
1580                                 LDAP_FREE(sval);
1581                                 if ( at->at_no_user_mod ) {
1582                                         *code = LDAP_SCHERR_DUPOPT;
1583                                         *errp = ss;
1584                                         ldap_attributetype_free(at);
1585                                         return(NULL);
1586                                 }
1587                                 at->at_no_user_mod = LDAP_SCHEMA_YES;
1588                                 parse_whsp(&ss);
1589                         } else if ( !strcmp(sval,"USAGE") ) {
1590                                 LDAP_FREE(sval);
1591                                 if ( seen_usage ) {
1592                                         *code = LDAP_SCHERR_DUPOPT;
1593                                         *errp = ss;
1594                                         ldap_attributetype_free(at);
1595                                         return(NULL);
1596                                 }
1597                                 seen_usage = 1;
1598                                 parse_whsp(&ss);
1599                                 kind = get_token(&ss,&sval);
1600                                 if ( kind != TK_BAREWORD ) {
1601                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1602                                         *errp = ss;
1603                                         LDAP_FREE(sval);
1604                                         ldap_attributetype_free(at);
1605                                         return NULL;
1606                                 }
1607                                 if ( !strcasecmp(sval,"userApplications") )
1608                                         at->at_usage =
1609                                             LDAP_SCHEMA_USER_APPLICATIONS;
1610                                 else if ( !strcasecmp(sval,"directoryOperation") )
1611                                         at->at_usage =
1612                                             LDAP_SCHEMA_DIRECTORY_OPERATION;
1613                                 else if ( !strcasecmp(sval,"distributedOperation") )
1614                                         at->at_usage =
1615                                             LDAP_SCHEMA_DISTRIBUTED_OPERATION;
1616                                 else if ( !strcasecmp(sval,"dSAOperation") )
1617                                         at->at_usage =
1618                                             LDAP_SCHEMA_DSA_OPERATION;
1619                                 else {
1620                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1621                                         *errp = ss;
1622                                         LDAP_FREE(sval);
1623                                         ldap_attributetype_free(at);
1624                                         return NULL;
1625                                 }
1626                                 LDAP_FREE(sval);
1627                                 parse_whsp(&ss);
1628                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1629                                 /* Should be parse_qdstrings */
1630                                 ext_vals = parse_qdescrs(&ss, code);
1631                                 if ( !ext_vals ) {
1632                                         *errp = ss;
1633                                         ldap_attributetype_free(at);
1634                                         return NULL;
1635                                 }
1636                                 if ( add_extension(&at->at_extensions,
1637                                                     sval, ext_vals) ) {
1638                                         *code = LDAP_SCHERR_OUTOFMEM;
1639                                         *errp = ss;
1640                                         LDAP_FREE(sval);
1641                                         ldap_attributetype_free(at);
1642                                         return NULL;
1643                                 }
1644                         } else {
1645                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1646                                 *errp = ss;
1647                                 LDAP_FREE(sval);
1648                                 ldap_attributetype_free(at);
1649                                 return NULL;
1650                         }
1651                         break;
1652                 default:
1653                         *code = LDAP_SCHERR_UNEXPTOKEN;
1654                         *errp = ss;
1655                         LDAP_FREE(sval);
1656                         ldap_attributetype_free(at);
1657                         return NULL;
1658                 }
1659         }
1660 }
1661
1662 void
1663 ldap_objectclass_free(LDAP_OBJECT_CLASS * oc)
1664 {
1665         LDAP_FREE(oc->oc_oid);
1666         LDAP_VFREE(oc->oc_names);
1667         LDAP_FREE(oc->oc_desc);
1668         LDAP_VFREE(oc->oc_sup_oids);
1669         LDAP_VFREE(oc->oc_at_oids_must);
1670         LDAP_VFREE(oc->oc_at_oids_may);
1671         free_extensions(oc->oc_extensions);
1672         LDAP_FREE(oc);
1673 }
1674
1675 LDAP_OBJECT_CLASS *
1676 ldap_str2objectclass( const char * s, int * code, const char ** errp )
1677 {
1678         int kind;
1679         const char * ss = s;
1680         char * sval;
1681         int be_liberal = 1;     /* Future additional argument */
1682         int seen_name = 0;
1683         int seen_desc = 0;
1684         int seen_obsolete = 0;
1685         int seen_sup = 0;
1686         int seen_kind = 0;
1687         int seen_must = 0;
1688         int seen_may = 0;
1689         LDAP_OBJECT_CLASS * oc;
1690         char ** ssdummy;
1691         const char * savepos;
1692
1693         if ( !s ) {
1694                 *code = LDAP_SCHERR_EMPTY;
1695                 *errp = "";
1696                 return NULL;
1697         }
1698
1699         *errp = s;
1700         oc = LDAP_CALLOC(1,sizeof(LDAP_OBJECT_CLASS));
1701
1702         if ( !oc ) {
1703                 *code = LDAP_SCHERR_OUTOFMEM;
1704                 return NULL;
1705         }
1706
1707         kind = get_token(&ss,&sval);
1708         if ( kind != TK_LEFTPAREN ) {
1709                 *code = LDAP_SCHERR_NOLEFTPAREN;
1710                 LDAP_FREE(sval);
1711                 ldap_objectclass_free(oc);
1712                 return NULL;
1713         }
1714
1715         /*
1716          * Definitions MUST begin with an OID in the numericoid format.
1717          * However, this routine is used by clients to parse the response
1718          * from servers and very well known servers will provide an OID
1719          * in the wrong format or even no OID at all.  We do our best to
1720          * extract info from those servers.
1721          */
1722         parse_whsp(&ss);
1723         savepos = ss;
1724         oc->oc_oid = parse_numericoid(&ss,code,0);
1725         if ( !oc->oc_oid ) {
1726                 if ( be_liberal ) {
1727                         /* Backtracking */
1728                         ss = savepos;
1729                         kind = get_token(&ss,&sval);
1730                         if ( kind == TK_BAREWORD ) {
1731                                 if ( !strcmp(sval, "NAME") ||
1732                                      !strcmp(sval, "DESC") ||
1733                                      !strcmp(sval, "OBSOLETE") ||
1734                                      !strcmp(sval, "SUP") ||
1735                                      !strcmp(sval, "ABSTRACT") ||
1736                                      !strcmp(sval, "STRUCTURAL") ||
1737                                      !strcmp(sval, "AUXILIARY") ||
1738                                      !strcmp(sval, "MUST") ||
1739                                      !strncmp(sval, "X-", 2) ) {
1740                                         /* Missing OID, backtrack */
1741                                         ss = savepos;
1742                                 } else {
1743                                         /* Non-numerical OID, ignore */
1744                                 }
1745                         }
1746                         LDAP_FREE(sval);
1747                 } else {
1748                         *errp = ss;
1749                         ldap_objectclass_free(oc);
1750                         return NULL;
1751                 }
1752         }
1753         parse_whsp(&ss);
1754
1755         /*
1756          * Beyond this point we will be liberal an accept the items
1757          * in any order.
1758          */
1759         while (1) {
1760                 kind = get_token(&ss,&sval);
1761                 switch (kind) {
1762                 case TK_EOS:
1763                         *code = LDAP_SCHERR_NORIGHTPAREN;
1764                         *errp = ss;
1765                         ldap_objectclass_free(oc);
1766                         return NULL;
1767                 case TK_RIGHTPAREN:
1768                         return oc;
1769                 case TK_BAREWORD:
1770                         if ( !strcmp(sval,"NAME") ) {
1771                                 LDAP_FREE(sval);
1772                                 if ( seen_name ) {
1773                                         *code = LDAP_SCHERR_DUPOPT;
1774                                         *errp = ss;
1775                                         ldap_objectclass_free(oc);
1776                                         return(NULL);
1777                                 }
1778                                 seen_name = 1;
1779                                 oc->oc_names = parse_qdescrs(&ss,code);
1780                                 if ( !oc->oc_names ) {
1781                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1782                                                 *code = LDAP_SCHERR_BADNAME;
1783                                         *errp = ss;
1784                                         ldap_objectclass_free(oc);
1785                                         return NULL;
1786                                 }
1787                         } else if ( !strcmp(sval,"DESC") ) {
1788                                 LDAP_FREE(sval);
1789                                 if ( seen_desc ) {
1790                                         *code = LDAP_SCHERR_DUPOPT;
1791                                         *errp = ss;
1792                                         ldap_objectclass_free(oc);
1793                                         return(NULL);
1794                                 }
1795                                 seen_desc = 1;
1796                                 parse_whsp(&ss);
1797                                 kind = get_token(&ss,&sval);
1798                                 if ( kind != TK_QDSTRING ) {
1799                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1800                                         *errp = ss;
1801                                         LDAP_FREE(sval);
1802                                         ldap_objectclass_free(oc);
1803                                         return NULL;
1804                                 }
1805                                 oc->oc_desc = sval;
1806                                 parse_whsp(&ss);
1807                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1808                                 LDAP_FREE(sval);
1809                                 if ( seen_obsolete ) {
1810                                         *code = LDAP_SCHERR_DUPOPT;
1811                                         *errp = ss;
1812                                         ldap_objectclass_free(oc);
1813                                         return(NULL);
1814                                 }
1815                                 seen_obsolete = 1;
1816                                 oc->oc_obsolete = LDAP_SCHEMA_YES;
1817                                 parse_whsp(&ss);
1818                         } else if ( !strcmp(sval,"SUP") ) {
1819                                 LDAP_FREE(sval);
1820                                 if ( seen_sup ) {
1821                                         *code = LDAP_SCHERR_DUPOPT;
1822                                         *errp = ss;
1823                                         ldap_objectclass_free(oc);
1824                                         return(NULL);
1825                                 }
1826                                 seen_sup = 1;
1827                                 oc->oc_sup_oids = parse_oids(&ss,
1828                                                              code,
1829                                                              be_liberal);
1830                                 if ( !oc->oc_sup_oids ) {
1831                                         *errp = ss;
1832                                         ldap_objectclass_free(oc);
1833                                         return NULL;
1834                                 }
1835                         } else if ( !strcmp(sval,"ABSTRACT") ) {
1836                                 LDAP_FREE(sval);
1837                                 if ( seen_kind ) {
1838                                         *code = LDAP_SCHERR_DUPOPT;
1839                                         *errp = ss;
1840                                         ldap_objectclass_free(oc);
1841                                         return(NULL);
1842                                 }
1843                                 seen_kind = 1;
1844                                 oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
1845                                 parse_whsp(&ss);
1846                         } else if ( !strcmp(sval,"STRUCTURAL") ) {
1847                                 LDAP_FREE(sval);
1848                                 if ( seen_kind ) {
1849                                         *code = LDAP_SCHERR_DUPOPT;
1850                                         *errp = ss;
1851                                         ldap_objectclass_free(oc);
1852                                         return(NULL);
1853                                 }
1854                                 seen_kind = 1;
1855                                 oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
1856                                 parse_whsp(&ss);
1857                         } else if ( !strcmp(sval,"AUXILIARY") ) {
1858                                 LDAP_FREE(sval);
1859                                 if ( seen_kind ) {
1860                                         *code = LDAP_SCHERR_DUPOPT;
1861                                         *errp = ss;
1862                                         ldap_objectclass_free(oc);
1863                                         return(NULL);
1864                                 }
1865                                 seen_kind = 1;
1866                                 oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
1867                                 parse_whsp(&ss);
1868                         } else if ( !strcmp(sval,"MUST") ) {
1869                                 LDAP_FREE(sval);
1870                                 if ( seen_must ) {
1871                                         *code = LDAP_SCHERR_DUPOPT;
1872                                         *errp = ss;
1873                                         ldap_objectclass_free(oc);
1874                                         return(NULL);
1875                                 }
1876                                 seen_must = 1;
1877                                 oc->oc_at_oids_must = parse_oids(&ss,code,0);
1878                                 if ( !oc->oc_at_oids_must ) {
1879                                         *errp = ss;
1880                                         ldap_objectclass_free(oc);
1881                                         return NULL;
1882                                 }
1883                                 parse_whsp(&ss);
1884                         } else if ( !strcmp(sval,"MAY") ) {
1885                                 LDAP_FREE(sval);
1886                                 if ( seen_may ) {
1887                                         *code = LDAP_SCHERR_DUPOPT;
1888                                         *errp = ss;
1889                                         ldap_objectclass_free(oc);
1890                                         return(NULL);
1891                                 }
1892                                 seen_may = 1;
1893                                 oc->oc_at_oids_may = parse_oids(&ss,code,0);
1894                                 if ( !oc->oc_at_oids_may ) {
1895                                         *errp = ss;
1896                                         ldap_objectclass_free(oc);
1897                                         return NULL;
1898                                 }
1899                                 parse_whsp(&ss);
1900                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1901                                 LDAP_FREE(sval);
1902                                 /* Should be parse_qdstrings */
1903                                 ssdummy = parse_qdescrs(&ss, code);
1904                                 if ( !ssdummy ) {
1905                                         *errp = ss;
1906                                         ldap_objectclass_free(oc);
1907                                         return NULL;
1908                                 }
1909                         } else {
1910                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1911                                 *errp = ss;
1912                                 LDAP_FREE(sval);
1913                                 ldap_objectclass_free(oc);
1914                                 return NULL;
1915                         }
1916                         break;
1917                 default:
1918                         *code = LDAP_SCHERR_UNEXPTOKEN;
1919                         *errp = ss;
1920                         LDAP_FREE(sval);
1921                         ldap_objectclass_free(oc);
1922                         return NULL;
1923                 }
1924         }
1925 }
1926
1927 static char *const err2text[] = {
1928         "",
1929         "Out of memory",
1930         "Unexpected token",
1931         "Missing opening parenthesis",
1932         "Missing closing parenthesis",
1933         "Expecting digit",
1934         "Expecting a name",
1935         "Bad description",
1936         "Bad superiors",
1937         "Duplicate option",
1938         "Unexpected end of data"
1939 };
1940
1941 char *
1942 ldap_scherr2str(int code)
1943 {
1944         if ( code < 1 || code >= (sizeof(err2text)/sizeof(char *)) ) {
1945                 return "Unknown error";
1946         } else {
1947                 return err2text[code];
1948         }
1949 }