]> git.sur5r.net Git - openldap/blob - libraries/libldap/schema.c
0871386060167c9ae233f1614bde05176d017061
[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         *token_val = NULL;
535         switch (**sp) {
536         case '\0':
537                 kind = TK_EOS;
538                 (*sp)++;
539                 break;
540         case '(':
541                 kind = TK_LEFTPAREN;
542                 (*sp)++;
543                 break;
544         case ')':
545                 kind = TK_RIGHTPAREN;
546                 (*sp)++;
547                 break;
548         case '$':
549                 kind = TK_DOLLAR;
550                 (*sp)++;
551                 break;
552         case '\'':
553                 kind = TK_QDSTRING;
554                 (*sp)++;
555                 p = *sp;
556                 while ( **sp != '\'' && **sp != '\0' )
557                         (*sp)++;
558                 if ( **sp == '\'' ) {
559                         q = *sp;
560                         res = LDAP_MALLOC(q-p+1);
561                         if ( !res ) {
562                                 kind = TK_OUTOFMEM;
563                         } else {
564                                 strncpy(res,p,q-p);
565                                 res[q-p] = '\0';
566                                 *token_val = res;
567                         }
568                         (*sp)++;
569                 } else {
570                         kind = TK_NOENDQUOTE;
571                 }
572                 break;
573         default:
574                 kind = TK_BAREWORD;
575                 p = *sp;
576                 while ( !isspace(**sp) &&
577                         **sp != '(' &&
578                         **sp != ')' &&
579                         **sp != '$' &&
580                         **sp != '\'' &&
581                         **sp != '\0' )
582                         (*sp)++;
583                 q = *sp;
584                 res = LDAP_MALLOC(q-p+1);
585                 if ( !res ) {
586                         kind = TK_OUTOFMEM;
587                 } else {
588                         strncpy(res,p,q-p);
589                         res[q-p] = '\0';
590                         *token_val = res;
591                 }
592                 break;
593 /*              kind = TK_UNEXPCHAR; */
594 /*              break; */
595         }
596         
597         return kind;
598 }
599
600 /* Gobble optional whitespace */
601 static void
602 parse_whsp(const char **sp)
603 {
604         while (isspace(**sp))
605                 (*sp)++;
606 }
607
608 /* TBC:!!
609  * General note for all parsers: to guarantee the algorithm halts they
610  * must always advance the pointer even when an error is found.  For
611  * this one is not that important since an error here is fatal at the
612  * upper layers, but it is a simple strategy that will not get in
613  * endless loops.
614  */
615
616 /* Parse a sequence of dot-separated decimal strings */
617 static char *
618 parse_numericoid(const char **sp, int *code, const int allow_quoted)
619 {
620         char * res;
621         const char * start = *sp;
622         int len;
623         int quoted = 0;
624
625         /* Netscape puts the SYNTAX value in quotes (incorrectly) */
626         if ( allow_quoted && **sp == '\'' ) {
627                 quoted = 1;
628                 (*sp)++;
629                 start++;
630         }
631         /* Each iteration of this loop gets one decimal string */
632         while (**sp) {
633                 if ( !isdigit(**sp) ) {
634                         /*
635                          * Initial char is not a digit or char after dot is
636                          * not a digit
637                          */
638                         *code = LDAP_SCHERR_NODIGIT;
639                         return NULL;
640                 }
641                 (*sp)++;
642                 while ( isdigit(**sp) )
643                         (*sp)++;
644                 if ( **sp != '.' )
645                         break;
646                 /* Otherwise, gobble the dot and loop again */
647                 (*sp)++;
648         }
649         /* Now *sp points at the char past the numericoid. Perfect. */
650         len = *sp - start;
651         res = LDAP_MALLOC(len+1);
652         if (!res) {
653                 *code = LDAP_SCHERR_OUTOFMEM;
654                 return(NULL);
655         }
656         strncpy(res,start,len);
657         res[len] = '\0';
658         if ( allow_quoted && quoted ) {
659                 if ( **sp == '\'' ) {
660                         (*sp)++;
661                 } else {
662                         *code = LDAP_SCHERR_UNEXPTOKEN;
663                         LDAP_FREE(res);
664                         return NULL;
665                 }
666         }
667         return(res);
668 }
669
670 /* Parse a qdescr or a list of them enclosed in () */
671 static char **
672 parse_qdescrs(const char **sp, int *code)
673 {
674         char ** res;
675         char ** res1;
676         int kind;
677         char * sval;
678         int size;
679         int pos;
680
681         parse_whsp(sp);
682         kind = get_token(sp,&sval);
683         if ( kind == TK_LEFTPAREN ) {
684                 /* Let's presume there will be at least 2 entries */
685                 size = 3;
686                 res = LDAP_CALLOC(3,sizeof(char *));
687                 if ( !res ) {
688                         *code = LDAP_SCHERR_OUTOFMEM;
689                         return NULL;
690                 }
691                 pos = 0;
692                 while (1) {
693                         parse_whsp(sp);
694                         kind = get_token(sp,&sval);
695                         if ( kind == TK_RIGHTPAREN )
696                                 break;
697                         if ( kind == TK_QDESCR ) {
698                                 if ( pos == size-2 ) {
699                                         size++;
700                                         res1 = LDAP_REALLOC(res,size*sizeof(char *));
701                                         if ( !res1 ) {
702                                                 LDAP_VFREE(res);
703                                                 LDAP_FREE(sval);
704                                                 *code = LDAP_SCHERR_OUTOFMEM;
705                                                 return(NULL);
706                                         }
707                                         res = res1;
708                                 }
709                                 res[pos] = sval;
710                                 pos++;
711                                 parse_whsp(sp);
712                         } else {
713                                 LDAP_VFREE(res);
714                                 LDAP_FREE(sval);
715                                 *code = LDAP_SCHERR_UNEXPTOKEN;
716                                 return(NULL);
717                         }
718                 }
719                 res[pos] = NULL;
720                 parse_whsp(sp);
721                 return(res);
722         } else if ( kind == TK_QDESCR ) {
723                 res = LDAP_CALLOC(2,sizeof(char *));
724                 if ( !res ) {
725                         *code = LDAP_SCHERR_OUTOFMEM;
726                         return NULL;
727                 }
728                 res[0] = sval;
729                 res[1] = NULL;
730                 parse_whsp(sp);
731                 return res;
732         } else {
733                 LDAP_FREE(sval);
734                 *code = LDAP_SCHERR_BADNAME;
735                 return NULL;
736         }
737 }
738
739 /* Parse a woid */
740 static char *
741 parse_woid(const char **sp, int *code)
742 {
743         char * sval;
744         int kind;
745
746         parse_whsp(sp);
747         kind = get_token(sp, &sval);
748         if ( kind != TK_BAREWORD ) {
749                 LDAP_FREE(sval);
750                 *code = LDAP_SCHERR_UNEXPTOKEN;
751                 return NULL;
752         }
753         parse_whsp(sp);
754         return sval;
755 }
756
757 /* Parse a noidlen */
758 static char *
759 parse_noidlen(const char **sp, int *code, int *len, int allow_quoted)
760 {
761         char * sval;
762         int quoted = 0;
763
764         *len = 0;
765         /* Netscape puts the SYNTAX value in quotes (incorrectly) */
766         if ( allow_quoted && **sp == '\'' ) {
767                 quoted = 1;
768                 (*sp)++;
769         }
770         sval = parse_numericoid(sp, code, 0);
771         if ( !sval ) {
772                 return NULL;
773         }
774         if ( **sp == '{' ) {
775                 (*sp)++;
776                 *len = atoi(*sp);
777                 while ( isdigit(**sp) )
778                         (*sp)++;
779                 if ( **sp != '}' ) {
780                         *code = LDAP_SCHERR_UNEXPTOKEN;
781                         LDAP_FREE(sval);
782                         return NULL;
783                 }
784                 (*sp)++;
785         }               
786         if ( allow_quoted && quoted ) {
787                 if ( **sp == '\'' ) {
788                         (*sp)++;
789                 } else {
790                         *code = LDAP_SCHERR_UNEXPTOKEN;
791                         LDAP_FREE(sval);
792                         return NULL;
793                 }
794         }
795         return sval;
796 }
797
798 /*
799  * Next routine will accept a qdstring in place of an oid if
800  * allow_quoted is set.  This is necessary to interoperate with
801  * Netscape Directory server that will improperly quote each oid (at
802  * least those of the descr kind) in the SUP clause.
803  */
804
805 /* Parse a woid or a $-separated list of them enclosed in () */
806 static char **
807 parse_oids(const char **sp, int *code, const int allow_quoted)
808 {
809         char ** res;
810         char ** res1;
811         int kind;
812         char * sval;
813         int size;
814         int pos;
815
816         /*
817          * Strictly speaking, doing this here accepts whsp before the
818          * ( at the begining of an oidlist, but this is harmless.  Also,
819          * we are very liberal in what we accept as an OID.  Maybe
820          * refine later.
821          */
822         parse_whsp(sp);
823         kind = get_token(sp,&sval);
824         if ( kind == TK_LEFTPAREN ) {
825                 /* Let's presume there will be at least 2 entries */
826                 size = 3;
827                 res = LDAP_CALLOC(3,sizeof(char *));
828                 if ( !res ) {
829                         *code = LDAP_SCHERR_OUTOFMEM;
830                         return NULL;
831                 }
832                 pos = 0;
833                 parse_whsp(sp);
834                 kind = get_token(sp,&sval);
835                 if ( kind == TK_BAREWORD ||
836                      ( allow_quoted && kind == TK_QDSTRING ) ) {
837                         res[pos] = sval;
838                         pos++;
839                 } else {
840                         *code = LDAP_SCHERR_UNEXPTOKEN;
841                         LDAP_FREE(sval);
842                         LDAP_VFREE(res);
843                         return NULL;
844                 }
845                 parse_whsp(sp);
846                 while (1) {
847                         kind = get_token(sp,&sval);
848                         if ( kind == TK_RIGHTPAREN )
849                                 break;
850                         if ( kind == TK_DOLLAR ) {
851                                 parse_whsp(sp);
852                                 kind = get_token(sp,&sval);
853                                 if ( kind == TK_BAREWORD ||
854                                      ( allow_quoted &&
855                                        kind == TK_QDSTRING ) ) {
856                                         if ( pos == size-2 ) {
857                                                 size++;
858                                                 res1 = LDAP_REALLOC(res,size*sizeof(char *));
859                                                 if ( !res1 ) {
860                                                         LDAP_FREE(sval);
861                                                         LDAP_VFREE(res);
862                                                         *code = LDAP_SCHERR_OUTOFMEM;
863                                                         return(NULL);
864                                                 }
865                                                 res = res1;
866                                         }
867                                         res[pos] = sval;
868                                         pos++;
869                                 } else {
870                                         *code = LDAP_SCHERR_UNEXPTOKEN;
871                                         LDAP_FREE(sval);
872                                         LDAP_VFREE(res);
873                                         return NULL;
874                                 }
875                                 parse_whsp(sp);
876                         } else {
877                                 *code = LDAP_SCHERR_UNEXPTOKEN;
878                                 LDAP_FREE(sval);
879                                 LDAP_VFREE(res);
880                                 return NULL;
881                         }
882                 }
883                 res[pos] = NULL;
884                 parse_whsp(sp);
885                 return(res);
886         } else if ( kind == TK_BAREWORD ||
887                     ( allow_quoted && kind == TK_QDSTRING ) ) {
888                 res = LDAP_CALLOC(2,sizeof(char *));
889                 if ( !res ) {
890                         LDAP_FREE(sval);
891                         *code = LDAP_SCHERR_OUTOFMEM;
892                         return NULL;
893                 }
894                 res[0] = sval;
895                 res[1] = NULL;
896                 parse_whsp(sp);
897                 return res;
898         } else {
899                 LDAP_FREE(sval);
900                 *code = LDAP_SCHERR_BADNAME;
901                 return NULL;
902         }
903 }
904
905 void
906 ldap_syntax_free( LDAP_SYNTAX * syn )
907 {
908         LDAP_FREE(syn->syn_oid);
909         LDAP_FREE(syn->syn_desc);
910         LDAP_FREE(syn);
911 }
912
913 LDAP_SYNTAX *
914 ldap_str2syntax( const char * s, int * code, const char ** errp )
915 {
916         int kind;
917         const char * ss = s;
918         char * sval;
919         int seen_desc = 0;
920         LDAP_SYNTAX * syn;
921         char ** ssdummy;
922
923         if ( !s ) {
924                 *code = LDAP_SCHERR_EMPTY;
925                 *errp = "";
926                 return NULL;
927         }
928
929         *errp = s;
930         syn = LDAP_CALLOC(1,sizeof(LDAP_SYNTAX));
931
932         if ( !syn ) {
933                 *code = LDAP_SCHERR_OUTOFMEM;
934                 return NULL;
935         }
936
937         kind = get_token(&ss,&sval);
938         if ( kind != TK_LEFTPAREN ) {
939                 LDAP_FREE(sval);
940                 *code = LDAP_SCHERR_NOLEFTPAREN;
941                 ldap_syntax_free(syn);
942                 return NULL;
943         }
944
945         parse_whsp(&ss);
946         syn->syn_oid = parse_numericoid(&ss,code,0);
947         if ( !syn->syn_oid ) {
948                 *errp = ss;
949                 ldap_syntax_free(syn);
950                 return NULL;
951         }
952         parse_whsp(&ss);
953
954         /*
955          * Beyond this point we will be liberal and accept the items
956          * in any order.
957          */
958         while (1) {
959                 kind = get_token(&ss,&sval);
960                 switch (kind) {
961                 case TK_EOS:
962                         *code = LDAP_SCHERR_NORIGHTPAREN;
963                         *errp = ss;
964                         ldap_syntax_free(syn);
965                         return NULL;
966                 case TK_RIGHTPAREN:
967                         return syn;
968                 case TK_BAREWORD:
969                         if ( !strcmp(sval,"DESC") ) {
970                                 LDAP_FREE(sval);
971                                 if ( seen_desc ) {
972                                         *code = LDAP_SCHERR_DUPOPT;
973                                         *errp = ss;
974                                         ldap_syntax_free(syn);
975                                         return(NULL);
976                                 }
977                                 seen_desc = 1;
978                                 parse_whsp(&ss);
979                                 kind = get_token(&ss,&sval);
980                                 if ( kind != TK_QDSTRING ) {
981                                         *code = LDAP_SCHERR_UNEXPTOKEN;
982                                         *errp = ss;
983                                         LDAP_FREE(sval);
984                                         ldap_syntax_free(syn);
985                                         return NULL;
986                                 }
987                                 syn->syn_desc = sval;
988                                 parse_whsp(&ss);
989                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
990                                 LDAP_FREE(sval);
991                                 /* Should be parse_qdstrings */
992                                 ssdummy = parse_qdescrs(&ss, code);
993                                 if ( !ssdummy ) {
994                                         *errp = ss;
995                                         ldap_syntax_free(syn);
996                                         return NULL;
997                                 }
998                         } else {
999                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1000                                 *errp = ss;
1001                                 LDAP_FREE(sval);
1002                                 ldap_syntax_free(syn);
1003                                 return NULL;
1004                         }
1005                         break;
1006                 default:
1007                         *code = LDAP_SCHERR_UNEXPTOKEN;
1008                         *errp = ss;
1009                         LDAP_FREE(sval);
1010                         ldap_syntax_free(syn);
1011                         return NULL;
1012                 }
1013         }
1014 }
1015
1016 void
1017 ldap_matchingrule_free( LDAP_MATCHING_RULE * mr )
1018 {
1019         LDAP_FREE(mr->mr_oid);
1020         LDAP_VFREE(mr->mr_names);
1021         LDAP_FREE(mr->mr_desc);
1022         LDAP_FREE(mr->mr_syntax_oid);
1023         LDAP_FREE(mr);
1024 }
1025
1026 LDAP_MATCHING_RULE *
1027 ldap_str2matchingrule( const char * s, int * code, const char ** errp )
1028 {
1029         int kind;
1030         const char * ss = s;
1031         char * sval;
1032         int be_liberal = 1;     /* Future additional argument */
1033         int seen_name = 0;
1034         int seen_desc = 0;
1035         int seen_obsolete = 0;
1036         int seen_syntax = 0;
1037         LDAP_MATCHING_RULE * mr;
1038         char ** ssdummy;
1039         const char * savepos;
1040
1041         if ( !s ) {
1042                 *code = LDAP_SCHERR_EMPTY;
1043                 *errp = "";
1044                 return NULL;
1045         }
1046
1047         *errp = s;
1048         mr = LDAP_CALLOC(1,sizeof(LDAP_MATCHING_RULE));
1049
1050         if ( !mr ) {
1051                 *code = LDAP_SCHERR_OUTOFMEM;
1052                 return NULL;
1053         }
1054
1055         kind = get_token(&ss,&sval);
1056         if ( kind != TK_LEFTPAREN ) {
1057                 *code = LDAP_SCHERR_NOLEFTPAREN;
1058                 LDAP_FREE(sval);
1059                 ldap_matchingrule_free(mr);
1060                 return NULL;
1061         }
1062
1063         parse_whsp(&ss);
1064         savepos = ss;
1065         mr->mr_oid = parse_numericoid(&ss,code,be_liberal);
1066         if ( !mr->mr_oid ) {
1067                 if ( be_liberal ) {
1068                         /* Backtracking */
1069                         ss = savepos;
1070                         kind = get_token(&ss,&sval);
1071                         if ( kind == TK_BAREWORD ) {
1072                                 if ( !strcmp(sval, "NAME") ||
1073                                      !strcmp(sval, "DESC") ||
1074                                      !strcmp(sval, "OBSOLETE") ||
1075                                      !strcmp(sval, "SYNTAX") ||
1076                                      !strncmp(sval, "X-", 2) ) {
1077                                         /* Missing OID, backtrack */
1078                                         ss = savepos;
1079                                 } else {
1080                                         /* Non-numerical OID, ignore */
1081                                 }
1082                         }
1083                         LDAP_FREE(sval);
1084                 } else {
1085                         *errp = ss;
1086                         ldap_matchingrule_free(mr);
1087                         return NULL;
1088                 }
1089         }
1090         parse_whsp(&ss);
1091
1092         /*
1093          * Beyond this point we will be liberal and accept the items
1094          * in any order.
1095          */
1096         while (1) {
1097                 kind = get_token(&ss,&sval);
1098                 switch (kind) {
1099                 case TK_EOS:
1100                         *code = LDAP_SCHERR_NORIGHTPAREN;
1101                         *errp = ss;
1102                         ldap_matchingrule_free(mr);
1103                         return NULL;
1104                 case TK_RIGHTPAREN:
1105                         return mr;
1106                 case TK_BAREWORD:
1107                         if ( !strcmp(sval,"NAME") ) {
1108                                 LDAP_FREE(sval);
1109                                 if ( seen_name ) {
1110                                         *code = LDAP_SCHERR_DUPOPT;
1111                                         *errp = ss;
1112                                         ldap_matchingrule_free(mr);
1113                                         return(NULL);
1114                                 }
1115                                 seen_name = 1;
1116                                 mr->mr_names = parse_qdescrs(&ss,code);
1117                                 if ( !mr->mr_names ) {
1118                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1119                                                 *code = LDAP_SCHERR_BADNAME;
1120                                         *errp = ss;
1121                                         ldap_matchingrule_free(mr);
1122                                         return NULL;
1123                                 }
1124                         } else if ( !strcmp(sval,"DESC") ) {
1125                                 LDAP_FREE(sval);
1126                                 if ( seen_desc ) {
1127                                         *code = LDAP_SCHERR_DUPOPT;
1128                                         *errp = ss;
1129                                         ldap_matchingrule_free(mr);
1130                                         return(NULL);
1131                                 }
1132                                 seen_desc = 1;
1133                                 parse_whsp(&ss);
1134                                 kind = get_token(&ss,&sval);
1135                                 if ( kind != TK_QDSTRING ) {
1136                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1137                                         *errp = ss;
1138                                         LDAP_FREE(sval);
1139                                         ldap_matchingrule_free(mr);
1140                                         return NULL;
1141                                 }
1142                                 mr->mr_desc = sval;
1143                                 parse_whsp(&ss);
1144                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1145                                 LDAP_FREE(sval);
1146                                 if ( seen_obsolete ) {
1147                                         *code = LDAP_SCHERR_DUPOPT;
1148                                         *errp = ss;
1149                                         ldap_matchingrule_free(mr);
1150                                         return(NULL);
1151                                 }
1152                                 seen_obsolete = 1;
1153                                 mr->mr_obsolete = LDAP_SCHEMA_YES;
1154                                 parse_whsp(&ss);
1155                         } else if ( !strcmp(sval,"SYNTAX") ) {
1156                                 LDAP_FREE(sval);
1157                                 if ( seen_syntax ) {
1158                                         *code = LDAP_SCHERR_DUPOPT;
1159                                         *errp = ss;
1160                                         ldap_matchingrule_free(mr);
1161                                         return(NULL);
1162                                 }
1163                                 seen_syntax = 1;
1164                                 parse_whsp(&ss);
1165                                 mr->mr_syntax_oid =
1166                                         parse_numericoid(&ss,code,be_liberal);
1167                                 if ( !mr->mr_syntax_oid ) {
1168                                         *errp = ss;
1169                                         ldap_matchingrule_free(mr);
1170                                         return NULL;
1171                                 }
1172                                 parse_whsp(&ss);
1173                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1174                                 LDAP_FREE(sval);
1175                                 /* Should be parse_qdstrings */
1176                                 ssdummy = parse_qdescrs(&ss, code);
1177                                 if ( !ssdummy ) {
1178                                         *errp = ss;
1179                                         ldap_matchingrule_free(mr);
1180                                         return NULL;
1181                                 }
1182                         } else {
1183                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1184                                 *errp = ss;
1185                                 LDAP_FREE(sval);
1186                                 ldap_matchingrule_free(mr);
1187                                 return NULL;
1188                         }
1189                         break;
1190                 default:
1191                         *code = LDAP_SCHERR_UNEXPTOKEN;
1192                         *errp = ss;
1193                         LDAP_FREE(sval);
1194                         ldap_matchingrule_free(mr);
1195                         return NULL;
1196                 }
1197         }
1198 }
1199
1200 void
1201 ldap_attributetype_free(LDAP_ATTRIBUTE_TYPE * at)
1202 {
1203         LDAP_FREE(at->at_oid);
1204         LDAP_VFREE(at->at_names);
1205         LDAP_FREE(at->at_desc);
1206         LDAP_FREE(at->at_sup_oid);
1207         LDAP_FREE(at->at_equality_oid);
1208         LDAP_FREE(at->at_ordering_oid);
1209         LDAP_FREE(at->at_substr_oid);
1210         LDAP_FREE(at->at_syntax_oid);
1211         LDAP_FREE(at);
1212 }
1213
1214 LDAP_ATTRIBUTE_TYPE *
1215 ldap_str2attributetype( const char * s, int * code, const char ** errp )
1216 {
1217         int kind;
1218         const char * ss = s;
1219         char * sval;
1220         int be_liberal = 1;     /* Future additional argument */
1221         int seen_name = 0;
1222         int seen_desc = 0;
1223         int seen_obsolete = 0;
1224         int seen_sup = 0;
1225         int seen_equality = 0;
1226         int seen_ordering = 0;
1227         int seen_substr = 0;
1228         int seen_syntax = 0;
1229         int seen_usage = 0;
1230         int seen_kind = 0;
1231         int seen_must = 0;
1232         int seen_may = 0;
1233         LDAP_ATTRIBUTE_TYPE * at;
1234         char ** ssdummy;
1235         const char * savepos;
1236
1237         if ( !s ) {
1238                 *code = LDAP_SCHERR_EMPTY;
1239                 *errp = "";
1240                 return NULL;
1241         }
1242
1243         *errp = s;
1244         at = LDAP_CALLOC(1,sizeof(LDAP_ATTRIBUTE_TYPE));
1245
1246         if ( !at ) {
1247                 *code = LDAP_SCHERR_OUTOFMEM;
1248                 return NULL;
1249         }
1250
1251         kind = get_token(&ss,&sval);
1252         if ( kind != TK_LEFTPAREN ) {
1253                 *code = LDAP_SCHERR_NOLEFTPAREN;
1254                 LDAP_FREE(sval);
1255                 ldap_attributetype_free(at);
1256                 return NULL;
1257         }
1258
1259         /*
1260          * Definitions MUST begin with an OID in the numericoid format.
1261          * However, this routine is used by clients to parse the response
1262          * from servers and very well known servers will provide an OID
1263          * in the wrong format or even no OID at all.  We do our best to
1264          * extract info from those servers.
1265          */
1266         parse_whsp(&ss);
1267         savepos = ss;
1268         at->at_oid = parse_numericoid(&ss,code,0);
1269         if ( !at->at_oid ) {
1270                 if ( be_liberal ) {
1271                         /* Backtracking */
1272                         ss = savepos;
1273                         kind = get_token(&ss,&sval);
1274                         if ( kind == TK_BAREWORD ) {
1275                                 if ( !strcmp(sval, "NAME") ||
1276                                      !strcmp(sval, "DESC") ||
1277                                      !strcmp(sval, "OBSOLETE") ||
1278                                      !strcmp(sval, "SUP") ||
1279                                      !strcmp(sval, "EQUALITY") ||
1280                                      !strcmp(sval, "ORDERING") ||
1281                                      !strcmp(sval, "SUBSTR") ||
1282                                      !strcmp(sval, "SYNTAX") ||
1283                                      !strcmp(sval, "SINGLE-VALUE") ||
1284                                      !strcmp(sval, "COLLECTIVE") ||
1285                                      !strcmp(sval, "NO-USER-MODIFICATION") ||
1286                                      !strcmp(sval, "USAGE") ||
1287                                      !strncmp(sval, "X-", 2) ) {
1288                                         /* Missing OID, backtrack */
1289                                         ss = savepos;
1290                                 } else {
1291                                         /* Non-numerical OID, ignore */
1292                                 }
1293                         }
1294                         LDAP_FREE(sval);
1295                 } else {
1296                         *errp = ss;
1297                         ldap_attributetype_free(at);
1298                         return NULL;
1299                 }
1300         }
1301         parse_whsp(&ss);
1302
1303         /*
1304          * Beyond this point we will be liberal and accept the items
1305          * in any order.
1306          */
1307         while (1) {
1308                 kind = get_token(&ss,&sval);
1309                 switch (kind) {
1310                 case TK_EOS:
1311                         *code = LDAP_SCHERR_NORIGHTPAREN;
1312                         *errp = ss;
1313                         ldap_attributetype_free(at);
1314                         return NULL;
1315                 case TK_RIGHTPAREN:
1316                         return at;
1317                 case TK_BAREWORD:
1318                         if ( !strcmp(sval,"NAME") ) {
1319                                 LDAP_FREE(sval);
1320                                 if ( seen_name ) {
1321                                         *code = LDAP_SCHERR_DUPOPT;
1322                                         *errp = ss;
1323                                         ldap_attributetype_free(at);
1324                                         return(NULL);
1325                                 }
1326                                 seen_name = 1;
1327                                 at->at_names = parse_qdescrs(&ss,code);
1328                                 if ( !at->at_names ) {
1329                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1330                                                 *code = LDAP_SCHERR_BADNAME;
1331                                         *errp = ss;
1332                                         ldap_attributetype_free(at);
1333                                         return NULL;
1334                                 }
1335                         } else if ( !strcmp(sval,"DESC") ) {
1336                                 LDAP_FREE(sval);
1337                                 if ( seen_desc ) {
1338                                         *code = LDAP_SCHERR_DUPOPT;
1339                                         *errp = ss;
1340                                         ldap_attributetype_free(at);
1341                                         return(NULL);
1342                                 }
1343                                 seen_desc = 1;
1344                                 parse_whsp(&ss);
1345                                 kind = get_token(&ss,&sval);
1346                                 if ( kind != TK_QDSTRING ) {
1347                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1348                                         *errp = ss;
1349                                         LDAP_FREE(sval);
1350                                         ldap_attributetype_free(at);
1351                                         return NULL;
1352                                 }
1353                                 at->at_desc = sval;
1354                                 parse_whsp(&ss);
1355                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1356                                 LDAP_FREE(sval);
1357                                 if ( seen_obsolete ) {
1358                                         *code = LDAP_SCHERR_DUPOPT;
1359                                         *errp = ss;
1360                                         ldap_attributetype_free(at);
1361                                         return(NULL);
1362                                 }
1363                                 seen_obsolete = 1;
1364                                 at->at_obsolete = LDAP_SCHEMA_YES;
1365                                 parse_whsp(&ss);
1366                         } else if ( !strcmp(sval,"SUP") ) {
1367                                 LDAP_FREE(sval);
1368                                 if ( seen_sup ) {
1369                                         *code = LDAP_SCHERR_DUPOPT;
1370                                         *errp = ss;
1371                                         ldap_attributetype_free(at);
1372                                         return(NULL);
1373                                 }
1374                                 seen_sup = 1;
1375                                 at->at_sup_oid = parse_woid(&ss,code);
1376                                 if ( !at->at_sup_oid ) {
1377                                         *errp = ss;
1378                                         ldap_attributetype_free(at);
1379                                         return NULL;
1380                                 }
1381                         } else if ( !strcmp(sval,"EQUALITY") ) {
1382                                 LDAP_FREE(sval);
1383                                 if ( seen_equality ) {
1384                                         *code = LDAP_SCHERR_DUPOPT;
1385                                         *errp = ss;
1386                                         ldap_attributetype_free(at);
1387                                         return(NULL);
1388                                 }
1389                                 seen_equality = 1;
1390                                 at->at_equality_oid = parse_woid(&ss,code);
1391                                 if ( !at->at_equality_oid ) {
1392                                         *errp = ss;
1393                                         ldap_attributetype_free(at);
1394                                         return NULL;
1395                                 }
1396                         } else if ( !strcmp(sval,"ORDERING") ) {
1397                                 LDAP_FREE(sval);
1398                                 if ( seen_ordering ) {
1399                                         *code = LDAP_SCHERR_DUPOPT;
1400                                         *errp = ss;
1401                                         ldap_attributetype_free(at);
1402                                         return(NULL);
1403                                 }
1404                                 seen_ordering = 1;
1405                                 at->at_ordering_oid = parse_woid(&ss,code);
1406                                 if ( !at->at_ordering_oid ) {
1407                                         *errp = ss;
1408                                         ldap_attributetype_free(at);
1409                                         return NULL;
1410                                 }
1411                         } else if ( !strcmp(sval,"SUBSTR") ) {
1412                                 LDAP_FREE(sval);
1413                                 if ( seen_substr ) {
1414                                         *code = LDAP_SCHERR_DUPOPT;
1415                                         *errp = ss;
1416                                         ldap_attributetype_free(at);
1417                                         return(NULL);
1418                                 }
1419                                 seen_substr = 1;
1420                                 at->at_substr_oid = parse_woid(&ss,code);
1421                                 if ( !at->at_substr_oid ) {
1422                                         *errp = ss;
1423                                         ldap_attributetype_free(at);
1424                                         return NULL;
1425                                 }
1426                         } else if ( !strcmp(sval,"SYNTAX") ) {
1427                                 LDAP_FREE(sval);
1428                                 if ( seen_syntax ) {
1429                                         *code = LDAP_SCHERR_DUPOPT;
1430                                         *errp = ss;
1431                                         ldap_attributetype_free(at);
1432                                         return(NULL);
1433                                 }
1434                                 seen_syntax = 1;
1435                                 parse_whsp(&ss);
1436                                 at->at_syntax_oid =
1437                                         parse_noidlen(&ss,
1438                                                       code,
1439                                                       &at->at_syntax_len,
1440                                                       be_liberal);
1441                                 if ( !at->at_syntax_oid ) {
1442                                         *errp = ss;
1443                                         ldap_attributetype_free(at);
1444                                         return NULL;
1445                                 }
1446                                 parse_whsp(&ss);
1447                         } else if ( !strcmp(sval,"SINGLE-VALUE") ) {
1448                                 LDAP_FREE(sval);
1449                                 if ( at->at_single_value ) {
1450                                         *code = LDAP_SCHERR_DUPOPT;
1451                                         *errp = ss;
1452                                         ldap_attributetype_free(at);
1453                                         return(NULL);
1454                                 }
1455                                 at->at_single_value = LDAP_SCHEMA_YES;
1456                                 parse_whsp(&ss);
1457                         } else if ( !strcmp(sval,"COLLECTIVE") ) {
1458                                 LDAP_FREE(sval);
1459                                 if ( at->at_collective ) {
1460                                         *code = LDAP_SCHERR_DUPOPT;
1461                                         *errp = ss;
1462                                         ldap_attributetype_free(at);
1463                                         return(NULL);
1464                                 }
1465                                 at->at_collective = LDAP_SCHEMA_YES;
1466                                 parse_whsp(&ss);
1467                         } else if ( !strcmp(sval,"NO-USER-MODIFICATION") ) {
1468                                 LDAP_FREE(sval);
1469                                 if ( at->at_no_user_mod ) {
1470                                         *code = LDAP_SCHERR_DUPOPT;
1471                                         *errp = ss;
1472                                         ldap_attributetype_free(at);
1473                                         return(NULL);
1474                                 }
1475                                 at->at_no_user_mod = LDAP_SCHEMA_YES;
1476                                 parse_whsp(&ss);
1477                         } else if ( !strcmp(sval,"USAGE") ) {
1478                                 LDAP_FREE(sval);
1479                                 if ( seen_usage ) {
1480                                         *code = LDAP_SCHERR_DUPOPT;
1481                                         *errp = ss;
1482                                         ldap_attributetype_free(at);
1483                                         return(NULL);
1484                                 }
1485                                 seen_usage = 1;
1486                                 parse_whsp(&ss);
1487                                 kind = get_token(&ss,&sval);
1488                                 if ( kind != TK_BAREWORD ) {
1489                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1490                                         *errp = ss;
1491                                         LDAP_FREE(sval);
1492                                         ldap_attributetype_free(at);
1493                                         return NULL;
1494                                 }
1495                                 if ( !strcasecmp(sval,"userApplications") )
1496                                         at->at_usage =
1497                                             LDAP_SCHEMA_USER_APPLICATIONS;
1498                                 else if ( !strcasecmp(sval,"directoryOperation") )
1499                                         at->at_usage =
1500                                             LDAP_SCHEMA_DIRECTORY_OPERATION;
1501                                 else if ( !strcasecmp(sval,"distributedOperation") )
1502                                         at->at_usage =
1503                                             LDAP_SCHEMA_DISTRIBUTED_OPERATION;
1504                                 else if ( !strcasecmp(sval,"dSAOperation") )
1505                                         at->at_usage =
1506                                             LDAP_SCHEMA_DSA_OPERATION;
1507                                 else {
1508                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1509                                         *errp = ss;
1510                                         LDAP_FREE(sval);
1511                                         ldap_attributetype_free(at);
1512                                         return NULL;
1513                                 }
1514                                 LDAP_FREE(sval);
1515                                 parse_whsp(&ss);
1516                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1517                                 LDAP_FREE(sval);
1518                                 /* Should be parse_qdstrings */
1519                                 ssdummy = parse_qdescrs(&ss, code);
1520                                 if ( !ssdummy ) {
1521                                         *errp = ss;
1522                                         ldap_attributetype_free(at);
1523                                         return NULL;
1524                                 }
1525                         } else {
1526                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1527                                 *errp = ss;
1528                                 LDAP_FREE(sval);
1529                                 ldap_attributetype_free(at);
1530                                 return NULL;
1531                         }
1532                         break;
1533                 default:
1534                         *code = LDAP_SCHERR_UNEXPTOKEN;
1535                         *errp = ss;
1536                         LDAP_FREE(sval);
1537                         ldap_attributetype_free(at);
1538                         return NULL;
1539                 }
1540         }
1541 }
1542
1543 void
1544 ldap_objectclass_free(LDAP_OBJECT_CLASS * oc)
1545 {
1546         LDAP_FREE(oc->oc_oid);
1547         LDAP_VFREE(oc->oc_names);
1548         LDAP_FREE(oc->oc_desc);
1549         LDAP_VFREE(oc->oc_sup_oids);
1550         LDAP_VFREE(oc->oc_at_oids_must);
1551         LDAP_VFREE(oc->oc_at_oids_may);
1552         LDAP_FREE(oc);
1553 }
1554
1555 LDAP_OBJECT_CLASS *
1556 ldap_str2objectclass( const char * s, int * code, const char ** errp )
1557 {
1558         int kind;
1559         const char * ss = s;
1560         char * sval;
1561         int be_liberal = 1;     /* Future additional argument */
1562         int seen_name = 0;
1563         int seen_desc = 0;
1564         int seen_obsolete = 0;
1565         int seen_sup = 0;
1566         int seen_kind = 0;
1567         int seen_must = 0;
1568         int seen_may = 0;
1569         LDAP_OBJECT_CLASS * oc;
1570         char ** ssdummy;
1571         const char * savepos;
1572
1573         if ( !s ) {
1574                 *code = LDAP_SCHERR_EMPTY;
1575                 *errp = "";
1576                 return NULL;
1577         }
1578
1579         *errp = s;
1580         oc = LDAP_CALLOC(1,sizeof(LDAP_OBJECT_CLASS));
1581
1582         if ( !oc ) {
1583                 *code = LDAP_SCHERR_OUTOFMEM;
1584                 return NULL;
1585         }
1586
1587         kind = get_token(&ss,&sval);
1588         if ( kind != TK_LEFTPAREN ) {
1589                 *code = LDAP_SCHERR_NOLEFTPAREN;
1590                 LDAP_FREE(sval);
1591                 ldap_objectclass_free(oc);
1592                 return NULL;
1593         }
1594
1595         /*
1596          * Definitions MUST begin with an OID in the numericoid format.
1597          * However, this routine is used by clients to parse the response
1598          * from servers and very well known servers will provide an OID
1599          * in the wrong format or even no OID at all.  We do our best to
1600          * extract info from those servers.
1601          */
1602         parse_whsp(&ss);
1603         savepos = ss;
1604         oc->oc_oid = parse_numericoid(&ss,code,0);
1605         if ( !oc->oc_oid ) {
1606                 if ( be_liberal ) {
1607                         /* Backtracking */
1608                         ss = savepos;
1609                         kind = get_token(&ss,&sval);
1610                         if ( kind == TK_BAREWORD ) {
1611                                 if ( !strcmp(sval, "NAME") ||
1612                                      !strcmp(sval, "DESC") ||
1613                                      !strcmp(sval, "OBSOLETE") ||
1614                                      !strcmp(sval, "SUP") ||
1615                                      !strcmp(sval, "ABSTRACT") ||
1616                                      !strcmp(sval, "STRUCTURAL") ||
1617                                      !strcmp(sval, "AUXILIARY") ||
1618                                      !strcmp(sval, "MUST") ||
1619                                      !strncmp(sval, "X-", 2) ) {
1620                                         /* Missing OID, backtrack */
1621                                         ss = savepos;
1622                                 } else {
1623                                         /* Non-numerical OID, ignore */
1624                                 }
1625                         }
1626                         LDAP_FREE(sval);
1627                 } else {
1628                         *errp = ss;
1629                         ldap_objectclass_free(oc);
1630                         return NULL;
1631                 }
1632         }
1633         parse_whsp(&ss);
1634
1635         /*
1636          * Beyond this point we will be liberal an accept the items
1637          * in any order.
1638          */
1639         while (1) {
1640                 kind = get_token(&ss,&sval);
1641                 switch (kind) {
1642                 case TK_EOS:
1643                         *code = LDAP_SCHERR_NORIGHTPAREN;
1644                         *errp = ss;
1645                         ldap_objectclass_free(oc);
1646                         return NULL;
1647                 case TK_RIGHTPAREN:
1648                         return oc;
1649                 case TK_BAREWORD:
1650                         if ( !strcmp(sval,"NAME") ) {
1651                                 LDAP_FREE(sval);
1652                                 if ( seen_name ) {
1653                                         *code = LDAP_SCHERR_DUPOPT;
1654                                         *errp = ss;
1655                                         ldap_objectclass_free(oc);
1656                                         return(NULL);
1657                                 }
1658                                 seen_name = 1;
1659                                 oc->oc_names = parse_qdescrs(&ss,code);
1660                                 if ( !oc->oc_names ) {
1661                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1662                                                 *code = LDAP_SCHERR_BADNAME;
1663                                         *errp = ss;
1664                                         ldap_objectclass_free(oc);
1665                                         return NULL;
1666                                 }
1667                         } else if ( !strcmp(sval,"DESC") ) {
1668                                 LDAP_FREE(sval);
1669                                 if ( seen_desc ) {
1670                                         *code = LDAP_SCHERR_DUPOPT;
1671                                         *errp = ss;
1672                                         ldap_objectclass_free(oc);
1673                                         return(NULL);
1674                                 }
1675                                 seen_desc = 1;
1676                                 parse_whsp(&ss);
1677                                 kind = get_token(&ss,&sval);
1678                                 if ( kind != TK_QDSTRING ) {
1679                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1680                                         *errp = ss;
1681                                         LDAP_FREE(sval);
1682                                         ldap_objectclass_free(oc);
1683                                         return NULL;
1684                                 }
1685                                 oc->oc_desc = sval;
1686                                 parse_whsp(&ss);
1687                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1688                                 LDAP_FREE(sval);
1689                                 if ( seen_obsolete ) {
1690                                         *code = LDAP_SCHERR_DUPOPT;
1691                                         *errp = ss;
1692                                         ldap_objectclass_free(oc);
1693                                         return(NULL);
1694                                 }
1695                                 seen_obsolete = 1;
1696                                 oc->oc_obsolete = LDAP_SCHEMA_YES;
1697                                 parse_whsp(&ss);
1698                         } else if ( !strcmp(sval,"SUP") ) {
1699                                 LDAP_FREE(sval);
1700                                 if ( seen_sup ) {
1701                                         *code = LDAP_SCHERR_DUPOPT;
1702                                         *errp = ss;
1703                                         ldap_objectclass_free(oc);
1704                                         return(NULL);
1705                                 }
1706                                 seen_sup = 1;
1707                                 oc->oc_sup_oids = parse_oids(&ss,
1708                                                              code,
1709                                                              be_liberal);
1710                                 if ( !oc->oc_sup_oids ) {
1711                                         *errp = ss;
1712                                         ldap_objectclass_free(oc);
1713                                         return NULL;
1714                                 }
1715                         } else if ( !strcmp(sval,"ABSTRACT") ) {
1716                                 LDAP_FREE(sval);
1717                                 if ( seen_kind ) {
1718                                         *code = LDAP_SCHERR_DUPOPT;
1719                                         *errp = ss;
1720                                         ldap_objectclass_free(oc);
1721                                         return(NULL);
1722                                 }
1723                                 seen_kind = 1;
1724                                 oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
1725                                 parse_whsp(&ss);
1726                         } else if ( !strcmp(sval,"STRUCTURAL") ) {
1727                                 LDAP_FREE(sval);
1728                                 if ( seen_kind ) {
1729                                         *code = LDAP_SCHERR_DUPOPT;
1730                                         *errp = ss;
1731                                         ldap_objectclass_free(oc);
1732                                         return(NULL);
1733                                 }
1734                                 seen_kind = 1;
1735                                 oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
1736                                 parse_whsp(&ss);
1737                         } else if ( !strcmp(sval,"AUXILIARY") ) {
1738                                 LDAP_FREE(sval);
1739                                 if ( seen_kind ) {
1740                                         *code = LDAP_SCHERR_DUPOPT;
1741                                         *errp = ss;
1742                                         ldap_objectclass_free(oc);
1743                                         return(NULL);
1744                                 }
1745                                 seen_kind = 1;
1746                                 oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
1747                                 parse_whsp(&ss);
1748                         } else if ( !strcmp(sval,"MUST") ) {
1749                                 LDAP_FREE(sval);
1750                                 if ( seen_must ) {
1751                                         *code = LDAP_SCHERR_DUPOPT;
1752                                         *errp = ss;
1753                                         ldap_objectclass_free(oc);
1754                                         return(NULL);
1755                                 }
1756                                 seen_must = 1;
1757                                 oc->oc_at_oids_must = parse_oids(&ss,code,0);
1758                                 if ( !oc->oc_at_oids_must ) {
1759                                         *errp = ss;
1760                                         ldap_objectclass_free(oc);
1761                                         return NULL;
1762                                 }
1763                                 parse_whsp(&ss);
1764                         } else if ( !strcmp(sval,"MAY") ) {
1765                                 LDAP_FREE(sval);
1766                                 if ( seen_may ) {
1767                                         *code = LDAP_SCHERR_DUPOPT;
1768                                         *errp = ss;
1769                                         ldap_objectclass_free(oc);
1770                                         return(NULL);
1771                                 }
1772                                 seen_may = 1;
1773                                 oc->oc_at_oids_may = parse_oids(&ss,code,0);
1774                                 if ( !oc->oc_at_oids_may ) {
1775                                         *errp = ss;
1776                                         ldap_objectclass_free(oc);
1777                                         return NULL;
1778                                 }
1779                                 parse_whsp(&ss);
1780                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1781                                 LDAP_FREE(sval);
1782                                 /* Should be parse_qdstrings */
1783                                 ssdummy = parse_qdescrs(&ss, code);
1784                                 if ( !ssdummy ) {
1785                                         *errp = ss;
1786                                         ldap_objectclass_free(oc);
1787                                         return NULL;
1788                                 }
1789                         } else {
1790                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1791                                 *errp = ss;
1792                                 LDAP_FREE(sval);
1793                                 ldap_objectclass_free(oc);
1794                                 return NULL;
1795                         }
1796                         break;
1797                 default:
1798                         *code = LDAP_SCHERR_UNEXPTOKEN;
1799                         *errp = ss;
1800                         LDAP_FREE(sval);
1801                         ldap_objectclass_free(oc);
1802                         return NULL;
1803                 }
1804         }
1805 }
1806
1807 static char *err2text[] = {
1808         "",
1809         "Out of memory",
1810         "Unexpected token",
1811         "Missing opening parenthesis",
1812         "Missing closing parenthesis",
1813         "Expecting digit",
1814         "Expecting a name",
1815         "Bad description",
1816         "Bad superiors",
1817         "Duplicate option",
1818         "Unexpected end of data"
1819 };
1820
1821 char *
1822 ldap_scherr2str(int code)
1823 {
1824         if ( code < 1 || code >= (sizeof(err2text)/sizeof(char *)) ) {
1825                 return "Unknown error";
1826         } else {
1827                 return err2text[code];
1828         }
1829 }