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