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