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