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