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