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