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