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