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