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