]> git.sur5r.net Git - openldap/blob - libraries/libldap/schema.c
Expose ldap_create_control
[openldap] / libraries / libldap / schema.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1999-2000 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*
7  * schema.c:  parsing routines used by servers and clients to process
8  *      schema definitions
9  */
10
11 #include "portable.h"
12
13 #include <stdio.h>
14 #include <ac/stdlib.h>
15
16 #include <ac/string.h>
17 #include <ac/time.h>
18
19 #include "ldap-int.h"
20
21 #include <ldap_schema.h>
22
23 static LDAP_CONST char *
24 choose_name( char *names[], LDAP_CONST char *fallback )
25 {
26         return( (names != NULL && names[0] != NULL) ? names[0] : fallback );
27 }
28
29 LDAP_CONST char *
30 ldap_syntax2name( LDAPSyntax * syn )
31 {
32         return( syn->syn_oid );
33 }
34
35 LDAP_CONST char *
36 ldap_matchingrule2name( LDAPMatchingRule * mr )
37 {
38         return( choose_name( mr->mr_names, mr->mr_oid ) );
39 }
40
41 LDAP_CONST char *
42 ldap_attributetype2name( LDAPAttributeType * at )
43 {
44         return( choose_name( at->at_names, at->at_oid ) );
45 }
46
47 LDAP_CONST char *
48 ldap_objectclass2name( LDAPObjectClass * oc )
49 {
50         return( choose_name( oc->oc_names, oc->oc_oid ) );
51 }
52
53
54 /*
55  * When pretty printing the entities we will be appending to a buffer.
56  * Since checking for overflow, realloc'ing and checking if no error
57  * is extremely boring, we will use a protection layer that will let
58  * us blissfully ignore the error until the end.  This layer is
59  * implemented with the help of the next type.
60  */
61
62 typedef struct safe_string {
63         char * val;
64         ber_len_t size;
65         ber_len_t pos;
66         int at_whsp;
67 } safe_string;
68
69 static safe_string *
70 new_safe_string(int size)
71 {
72         safe_string * ss;
73         
74         ss = LDAP_MALLOC(sizeof(safe_string));
75         if ( !ss )
76                 return(NULL);
77
78         ss->val = LDAP_MALLOC(size);
79         if ( !ss->val ) {
80                 LDAP_FREE(ss);
81                 return(NULL);
82         }
83
84         ss->size = size;
85         ss->pos = 0;
86         ss->at_whsp = 0;
87
88         return ss;
89 }
90
91 static void
92 safe_string_free(safe_string * ss)
93 {
94         if ( !ss )
95                 return;
96         LDAP_FREE(ss->val);
97         LDAP_FREE(ss);
98 }
99
100 static char *
101 safe_string_val(safe_string * ss)
102 {
103         ss->val[ss->pos] = '\0';
104         return(ss->val);
105 }
106
107 static int
108 append_to_safe_string(safe_string * ss, char * s)
109 {
110         int l = strlen(s);
111         char * temp;
112
113         /*
114          * Some runaway process is trying to append to a string that
115          * overflowed and we could not extend.
116          */
117         if ( !ss->val )
118                 return -1;
119
120         /* We always make sure there is at least one position available */
121         if ( ss->pos + l >= ss->size-1 ) {
122                 ss->size *= 2;
123                 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, LDAPSchemaExtensionItem **extensions)
271 {
272         LDAPSchemaExtensionItem **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 LDAPSyntax * 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 LDAPMatchingRule * 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 LDAPObjectClass * 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 LDAPAttributeType * 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(LDAPSchemaExtensionItem ***extensions,
970               char * name, char ** values)
971 {
972         int n;
973         LDAPSchemaExtensionItem **tmp, *ext;
974
975         ext = LDAP_CALLOC(1, sizeof(LDAPSchemaExtensionItem));
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(LDAPSchemaExtensionItem *));
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(LDAPSchemaExtensionItem *));
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(LDAPSchemaExtensionItem **extensions)
1003 {
1004         LDAPSchemaExtensionItem **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( LDAPSyntax * syn )
1018 {
1019         LDAP_FREE(syn->syn_oid);
1020         LDAP_VFREE(syn->syn_names);
1021         LDAP_FREE(syn->syn_desc);
1022         free_extensions(syn->syn_extensions);
1023         LDAP_FREE(syn);
1024 }
1025
1026 LDAPSyntax *
1027 ldap_str2syntax( const char * s, int * code, const char ** errp, const int flags )
1028 {
1029         int kind;
1030         const char * ss = s;
1031         char * sval;
1032         int seen_name = 0;
1033         int seen_desc = 0;
1034         LDAPSyntax * 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(LDAPSyntax));
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,"NAME") ) {
1084                                 LDAP_FREE(sval);
1085                                 if ( seen_name ) {
1086                                         *code = LDAP_SCHERR_DUPOPT;
1087                                         *errp = ss;
1088                                         ldap_syntax_free(syn);
1089                                         return(NULL);
1090                                 }
1091                                 seen_name = 1;
1092                                 syn->syn_names = parse_qdescrs(&ss,code);
1093                                 if ( !syn->syn_names ) {
1094                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1095                                                 *code = LDAP_SCHERR_BADNAME;
1096                                         *errp = ss;
1097                                         ldap_syntax_free(syn);
1098                                         return NULL;
1099                                 }
1100                         } else if ( !strcmp(sval,"DESC") ) {
1101                                 LDAP_FREE(sval);
1102                                 if ( seen_desc ) {
1103                                         *code = LDAP_SCHERR_DUPOPT;
1104                                         *errp = ss;
1105                                         ldap_syntax_free(syn);
1106                                         return(NULL);
1107                                 }
1108                                 seen_desc = 1;
1109                                 parse_whsp(&ss);
1110                                 kind = get_token(&ss,&sval);
1111                                 if ( kind != TK_QDSTRING ) {
1112                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1113                                         *errp = ss;
1114                                         LDAP_FREE(sval);
1115                                         ldap_syntax_free(syn);
1116                                         return NULL;
1117                                 }
1118                                 syn->syn_desc = sval;
1119                                 parse_whsp(&ss);
1120                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1121                                 /* Should be parse_qdstrings */
1122                                 ext_vals = parse_qdescrs(&ss, code);
1123                                 if ( !ext_vals ) {
1124                                         *errp = ss;
1125                                         ldap_syntax_free(syn);
1126                                         return NULL;
1127                                 }
1128                                 if ( add_extension(&syn->syn_extensions,
1129                                                     sval, ext_vals) ) {
1130                                         *code = LDAP_SCHERR_OUTOFMEM;
1131                                         *errp = ss;
1132                                         LDAP_FREE(sval);
1133                                         ldap_syntax_free(syn);
1134                                         return NULL;
1135                                 }
1136                         } else {
1137                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1138                                 *errp = ss;
1139                                 LDAP_FREE(sval);
1140                                 ldap_syntax_free(syn);
1141                                 return NULL;
1142                         }
1143                         break;
1144                 default:
1145                         *code = LDAP_SCHERR_UNEXPTOKEN;
1146                         *errp = ss;
1147                         LDAP_FREE(sval);
1148                         ldap_syntax_free(syn);
1149                         return NULL;
1150                 }
1151         }
1152 }
1153
1154 void
1155 ldap_matchingrule_free( LDAPMatchingRule * mr )
1156 {
1157         LDAP_FREE(mr->mr_oid);
1158         LDAP_VFREE(mr->mr_names);
1159         LDAP_FREE(mr->mr_desc);
1160         LDAP_FREE(mr->mr_syntax_oid);
1161         free_extensions(mr->mr_extensions);
1162         LDAP_FREE(mr);
1163 }
1164
1165 LDAPMatchingRule *
1166 ldap_str2matchingrule( const char * s, int * code, const char ** errp, const int flags )
1167 {
1168         int kind;
1169         const char * ss = s;
1170         char * sval;
1171         int seen_name = 0;
1172         int seen_desc = 0;
1173         int seen_obsolete = 0;
1174         int seen_syntax = 0;
1175         LDAPMatchingRule * mr;
1176         char ** ext_vals;
1177         const char * savepos;
1178
1179         if ( !s ) {
1180                 *code = LDAP_SCHERR_EMPTY;
1181                 *errp = "";
1182                 return NULL;
1183         }
1184
1185         *errp = s;
1186         mr = LDAP_CALLOC(1,sizeof(LDAPMatchingRule));
1187
1188         if ( !mr ) {
1189                 *code = LDAP_SCHERR_OUTOFMEM;
1190                 return NULL;
1191         }
1192
1193         kind = get_token(&ss,&sval);
1194         if ( kind != TK_LEFTPAREN ) {
1195                 *code = LDAP_SCHERR_NOLEFTPAREN;
1196                 LDAP_FREE(sval);
1197                 ldap_matchingrule_free(mr);
1198                 return NULL;
1199         }
1200
1201         parse_whsp(&ss);
1202         savepos = ss;
1203         mr->mr_oid = parse_numericoid(&ss,code,flags);
1204         if ( !mr->mr_oid ) {
1205                 if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
1206                         /* Backtracking */
1207                         ss = savepos;
1208                         kind = get_token(&ss,&sval);
1209                         if ( kind == TK_BAREWORD ) {
1210                                 if ( !strcmp(sval, "NAME") ||
1211                                      !strcmp(sval, "DESC") ||
1212                                      !strcmp(sval, "OBSOLETE") ||
1213                                      !strcmp(sval, "SYNTAX") ||
1214                                      !strncmp(sval, "X-", 2) ) {
1215                                         /* Missing OID, backtrack */
1216                                         ss = savepos;
1217                                 } else {
1218                                         /* Non-numerical OID, ignore */
1219                                 }
1220                         }
1221                         LDAP_FREE(sval);
1222                 } else {
1223                         *errp = ss;
1224                         ldap_matchingrule_free(mr);
1225                         return NULL;
1226                 }
1227         }
1228         parse_whsp(&ss);
1229
1230         /*
1231          * Beyond this point we will be liberal and accept the items
1232          * in any order.
1233          */
1234         while (1) {
1235                 kind = get_token(&ss,&sval);
1236                 switch (kind) {
1237                 case TK_EOS:
1238                         *code = LDAP_SCHERR_NORIGHTPAREN;
1239                         *errp = ss;
1240                         ldap_matchingrule_free(mr);
1241                         return NULL;
1242                 case TK_RIGHTPAREN:
1243                         return mr;
1244                 case TK_BAREWORD:
1245                         if ( !strcmp(sval,"NAME") ) {
1246                                 LDAP_FREE(sval);
1247                                 if ( seen_name ) {
1248                                         *code = LDAP_SCHERR_DUPOPT;
1249                                         *errp = ss;
1250                                         ldap_matchingrule_free(mr);
1251                                         return(NULL);
1252                                 }
1253                                 seen_name = 1;
1254                                 mr->mr_names = parse_qdescrs(&ss,code);
1255                                 if ( !mr->mr_names ) {
1256                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1257                                                 *code = LDAP_SCHERR_BADNAME;
1258                                         *errp = ss;
1259                                         ldap_matchingrule_free(mr);
1260                                         return NULL;
1261                                 }
1262                         } else if ( !strcmp(sval,"DESC") ) {
1263                                 LDAP_FREE(sval);
1264                                 if ( seen_desc ) {
1265                                         *code = LDAP_SCHERR_DUPOPT;
1266                                         *errp = ss;
1267                                         ldap_matchingrule_free(mr);
1268                                         return(NULL);
1269                                 }
1270                                 seen_desc = 1;
1271                                 parse_whsp(&ss);
1272                                 kind = get_token(&ss,&sval);
1273                                 if ( kind != TK_QDSTRING ) {
1274                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1275                                         *errp = ss;
1276                                         LDAP_FREE(sval);
1277                                         ldap_matchingrule_free(mr);
1278                                         return NULL;
1279                                 }
1280                                 mr->mr_desc = sval;
1281                                 parse_whsp(&ss);
1282                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1283                                 LDAP_FREE(sval);
1284                                 if ( seen_obsolete ) {
1285                                         *code = LDAP_SCHERR_DUPOPT;
1286                                         *errp = ss;
1287                                         ldap_matchingrule_free(mr);
1288                                         return(NULL);
1289                                 }
1290                                 seen_obsolete = 1;
1291                                 mr->mr_obsolete = LDAP_SCHEMA_YES;
1292                                 parse_whsp(&ss);
1293                         } else if ( !strcmp(sval,"SYNTAX") ) {
1294                                 LDAP_FREE(sval);
1295                                 if ( seen_syntax ) {
1296                                         *code = LDAP_SCHERR_DUPOPT;
1297                                         *errp = ss;
1298                                         ldap_matchingrule_free(mr);
1299                                         return(NULL);
1300                                 }
1301                                 seen_syntax = 1;
1302                                 parse_whsp(&ss);
1303                                 mr->mr_syntax_oid =
1304                                         parse_numericoid(&ss,code,flags);
1305                                 if ( !mr->mr_syntax_oid ) {
1306                                         *errp = ss;
1307                                         ldap_matchingrule_free(mr);
1308                                         return NULL;
1309                                 }
1310                                 parse_whsp(&ss);
1311                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1312                                 /* Should be parse_qdstrings */
1313                                 ext_vals = parse_qdescrs(&ss, code);
1314                                 if ( !ext_vals ) {
1315                                         *errp = ss;
1316                                         ldap_matchingrule_free(mr);
1317                                         return NULL;
1318                                 }
1319                                 if ( add_extension(&mr->mr_extensions,
1320                                                     sval, ext_vals) ) {
1321                                         *code = LDAP_SCHERR_OUTOFMEM;
1322                                         *errp = ss;
1323                                         LDAP_FREE(sval);
1324                                         ldap_matchingrule_free(mr);
1325                                         return NULL;
1326                                 }
1327                         } else {
1328                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1329                                 *errp = ss;
1330                                 LDAP_FREE(sval);
1331                                 ldap_matchingrule_free(mr);
1332                                 return NULL;
1333                         }
1334                         break;
1335                 default:
1336                         *code = LDAP_SCHERR_UNEXPTOKEN;
1337                         *errp = ss;
1338                         LDAP_FREE(sval);
1339                         ldap_matchingrule_free(mr);
1340                         return NULL;
1341                 }
1342         }
1343 }
1344
1345 void
1346 ldap_attributetype_free(LDAPAttributeType * at)
1347 {
1348         LDAP_FREE(at->at_oid);
1349         LDAP_VFREE(at->at_names);
1350         LDAP_FREE(at->at_desc);
1351         LDAP_FREE(at->at_sup_oid);
1352         LDAP_FREE(at->at_equality_oid);
1353         LDAP_FREE(at->at_ordering_oid);
1354         LDAP_FREE(at->at_substr_oid);
1355         LDAP_FREE(at->at_syntax_oid);
1356         free_extensions(at->at_extensions);
1357         LDAP_FREE(at);
1358 }
1359
1360 LDAPAttributeType *
1361 ldap_str2attributetype( const char * s, int * code, const char ** errp, const int flags )
1362 {
1363         int kind;
1364         const char * ss = s;
1365         char * sval;
1366         int seen_name = 0;
1367         int seen_desc = 0;
1368         int seen_obsolete = 0;
1369         int seen_sup = 0;
1370         int seen_equality = 0;
1371         int seen_ordering = 0;
1372         int seen_substr = 0;
1373         int seen_syntax = 0;
1374         int seen_usage = 0;
1375         LDAPAttributeType * at;
1376         char ** ext_vals;
1377         const char * savepos;
1378
1379         if ( !s ) {
1380                 *code = LDAP_SCHERR_EMPTY;
1381                 *errp = "";
1382                 return NULL;
1383         }
1384
1385         *errp = s;
1386         at = LDAP_CALLOC(1,sizeof(LDAPAttributeType));
1387
1388         if ( !at ) {
1389                 *code = LDAP_SCHERR_OUTOFMEM;
1390                 return NULL;
1391         }
1392
1393         kind = get_token(&ss,&sval);
1394         if ( kind != TK_LEFTPAREN ) {
1395                 *code = LDAP_SCHERR_NOLEFTPAREN;
1396                 LDAP_FREE(sval);
1397                 ldap_attributetype_free(at);
1398                 return NULL;
1399         }
1400
1401         /*
1402          * Definitions MUST begin with an OID in the numericoid format.
1403          * However, this routine is used by clients to parse the response
1404          * from servers and very well known servers will provide an OID
1405          * in the wrong format or even no OID at all.  We do our best to
1406          * extract info from those servers.
1407          */
1408         parse_whsp(&ss);
1409         savepos = ss;
1410         at->at_oid = parse_numericoid(&ss,code,0);
1411         if ( !at->at_oid ) {
1412                 if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
1413                         /* Backtracking */
1414                         ss = savepos;
1415                         kind = get_token(&ss,&sval);
1416                         if ( kind == TK_BAREWORD ) {
1417                                 if ( !strcmp(sval, "NAME") ||
1418                                      !strcmp(sval, "DESC") ||
1419                                      !strcmp(sval, "OBSOLETE") ||
1420                                      !strcmp(sval, "SUP") ||
1421                                      !strcmp(sval, "EQUALITY") ||
1422                                      !strcmp(sval, "ORDERING") ||
1423                                      !strcmp(sval, "SUBSTR") ||
1424                                      !strcmp(sval, "SYNTAX") ||
1425                                      !strcmp(sval, "SINGLE-VALUE") ||
1426                                      !strcmp(sval, "COLLECTIVE") ||
1427                                      !strcmp(sval, "NO-USER-MODIFICATION") ||
1428                                      !strcmp(sval, "USAGE") ||
1429                                      !strncmp(sval, "X-", 2) ) {
1430                                         /* Missing OID, backtrack */
1431                                         ss = savepos;
1432                                 } else {
1433                                         /* Non-numerical OID, ignore */
1434                                 }
1435                         }
1436                         LDAP_FREE(sval);
1437                 } else {
1438                         *errp = ss;
1439                         ldap_attributetype_free(at);
1440                         return NULL;
1441                 }
1442         }
1443         parse_whsp(&ss);
1444
1445         /*
1446          * Beyond this point we will be liberal and accept the items
1447          * in any order.
1448          */
1449         while (1) {
1450                 kind = get_token(&ss,&sval);
1451                 switch (kind) {
1452                 case TK_EOS:
1453                         *code = LDAP_SCHERR_NORIGHTPAREN;
1454                         *errp = ss;
1455                         ldap_attributetype_free(at);
1456                         return NULL;
1457                 case TK_RIGHTPAREN:
1458                         return at;
1459                 case TK_BAREWORD:
1460                         if ( !strcmp(sval,"NAME") ) {
1461                                 LDAP_FREE(sval);
1462                                 if ( seen_name ) {
1463                                         *code = LDAP_SCHERR_DUPOPT;
1464                                         *errp = ss;
1465                                         ldap_attributetype_free(at);
1466                                         return(NULL);
1467                                 }
1468                                 seen_name = 1;
1469                                 at->at_names = parse_qdescrs(&ss,code);
1470                                 if ( !at->at_names ) {
1471                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1472                                                 *code = LDAP_SCHERR_BADNAME;
1473                                         *errp = ss;
1474                                         ldap_attributetype_free(at);
1475                                         return NULL;
1476                                 }
1477                         } else if ( !strcmp(sval,"DESC") ) {
1478                                 LDAP_FREE(sval);
1479                                 if ( seen_desc ) {
1480                                         *code = LDAP_SCHERR_DUPOPT;
1481                                         *errp = ss;
1482                                         ldap_attributetype_free(at);
1483                                         return(NULL);
1484                                 }
1485                                 seen_desc = 1;
1486                                 parse_whsp(&ss);
1487                                 kind = get_token(&ss,&sval);
1488                                 if ( kind != TK_QDSTRING ) {
1489                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1490                                         *errp = ss;
1491                                         LDAP_FREE(sval);
1492                                         ldap_attributetype_free(at);
1493                                         return NULL;
1494                                 }
1495                                 at->at_desc = sval;
1496                                 parse_whsp(&ss);
1497                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1498                                 LDAP_FREE(sval);
1499                                 if ( seen_obsolete ) {
1500                                         *code = LDAP_SCHERR_DUPOPT;
1501                                         *errp = ss;
1502                                         ldap_attributetype_free(at);
1503                                         return(NULL);
1504                                 }
1505                                 seen_obsolete = 1;
1506                                 at->at_obsolete = LDAP_SCHEMA_YES;
1507                                 parse_whsp(&ss);
1508                         } else if ( !strcmp(sval,"SUP") ) {
1509                                 LDAP_FREE(sval);
1510                                 if ( seen_sup ) {
1511                                         *code = LDAP_SCHERR_DUPOPT;
1512                                         *errp = ss;
1513                                         ldap_attributetype_free(at);
1514                                         return(NULL);
1515                                 }
1516                                 seen_sup = 1;
1517                                 at->at_sup_oid = parse_woid(&ss,code);
1518                                 if ( !at->at_sup_oid ) {
1519                                         *errp = ss;
1520                                         ldap_attributetype_free(at);
1521                                         return NULL;
1522                                 }
1523                         } else if ( !strcmp(sval,"EQUALITY") ) {
1524                                 LDAP_FREE(sval);
1525                                 if ( seen_equality ) {
1526                                         *code = LDAP_SCHERR_DUPOPT;
1527                                         *errp = ss;
1528                                         ldap_attributetype_free(at);
1529                                         return(NULL);
1530                                 }
1531                                 seen_equality = 1;
1532                                 at->at_equality_oid = parse_woid(&ss,code);
1533                                 if ( !at->at_equality_oid ) {
1534                                         *errp = ss;
1535                                         ldap_attributetype_free(at);
1536                                         return NULL;
1537                                 }
1538                         } else if ( !strcmp(sval,"ORDERING") ) {
1539                                 LDAP_FREE(sval);
1540                                 if ( seen_ordering ) {
1541                                         *code = LDAP_SCHERR_DUPOPT;
1542                                         *errp = ss;
1543                                         ldap_attributetype_free(at);
1544                                         return(NULL);
1545                                 }
1546                                 seen_ordering = 1;
1547                                 at->at_ordering_oid = parse_woid(&ss,code);
1548                                 if ( !at->at_ordering_oid ) {
1549                                         *errp = ss;
1550                                         ldap_attributetype_free(at);
1551                                         return NULL;
1552                                 }
1553                         } else if ( !strcmp(sval,"SUBSTR") ) {
1554                                 LDAP_FREE(sval);
1555                                 if ( seen_substr ) {
1556                                         *code = LDAP_SCHERR_DUPOPT;
1557                                         *errp = ss;
1558                                         ldap_attributetype_free(at);
1559                                         return(NULL);
1560                                 }
1561                                 seen_substr = 1;
1562                                 at->at_substr_oid = parse_woid(&ss,code);
1563                                 if ( !at->at_substr_oid ) {
1564                                         *errp = ss;
1565                                         ldap_attributetype_free(at);
1566                                         return NULL;
1567                                 }
1568                         } else if ( !strcmp(sval,"SYNTAX") ) {
1569                                 LDAP_FREE(sval);
1570                                 if ( seen_syntax ) {
1571                                         *code = LDAP_SCHERR_DUPOPT;
1572                                         *errp = ss;
1573                                         ldap_attributetype_free(at);
1574                                         return(NULL);
1575                                 }
1576                                 seen_syntax = 1;
1577                                 parse_whsp(&ss);
1578                                 at->at_syntax_oid =
1579                                         parse_noidlen(&ss,
1580                                                       code,
1581                                                       &at->at_syntax_len,
1582                                                       flags);
1583                                 if ( !at->at_syntax_oid ) {
1584                                         *errp = ss;
1585                                         ldap_attributetype_free(at);
1586                                         return NULL;
1587                                 }
1588                                 parse_whsp(&ss);
1589                         } else if ( !strcmp(sval,"SINGLE-VALUE") ) {
1590                                 LDAP_FREE(sval);
1591                                 if ( at->at_single_value ) {
1592                                         *code = LDAP_SCHERR_DUPOPT;
1593                                         *errp = ss;
1594                                         ldap_attributetype_free(at);
1595                                         return(NULL);
1596                                 }
1597                                 at->at_single_value = LDAP_SCHEMA_YES;
1598                                 parse_whsp(&ss);
1599                         } else if ( !strcmp(sval,"COLLECTIVE") ) {
1600                                 LDAP_FREE(sval);
1601                                 if ( at->at_collective ) {
1602                                         *code = LDAP_SCHERR_DUPOPT;
1603                                         *errp = ss;
1604                                         ldap_attributetype_free(at);
1605                                         return(NULL);
1606                                 }
1607                                 at->at_collective = LDAP_SCHEMA_YES;
1608                                 parse_whsp(&ss);
1609                         } else if ( !strcmp(sval,"NO-USER-MODIFICATION") ) {
1610                                 LDAP_FREE(sval);
1611                                 if ( at->at_no_user_mod ) {
1612                                         *code = LDAP_SCHERR_DUPOPT;
1613                                         *errp = ss;
1614                                         ldap_attributetype_free(at);
1615                                         return(NULL);
1616                                 }
1617                                 at->at_no_user_mod = LDAP_SCHEMA_YES;
1618                                 parse_whsp(&ss);
1619                         } else if ( !strcmp(sval,"USAGE") ) {
1620                                 LDAP_FREE(sval);
1621                                 if ( seen_usage ) {
1622                                         *code = LDAP_SCHERR_DUPOPT;
1623                                         *errp = ss;
1624                                         ldap_attributetype_free(at);
1625                                         return(NULL);
1626                                 }
1627                                 seen_usage = 1;
1628                                 parse_whsp(&ss);
1629                                 kind = get_token(&ss,&sval);
1630                                 if ( kind != TK_BAREWORD ) {
1631                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1632                                         *errp = ss;
1633                                         LDAP_FREE(sval);
1634                                         ldap_attributetype_free(at);
1635                                         return NULL;
1636                                 }
1637                                 if ( !strcasecmp(sval,"userApplications") )
1638                                         at->at_usage =
1639                                             LDAP_SCHEMA_USER_APPLICATIONS;
1640                                 else if ( !strcasecmp(sval,"directoryOperation") )
1641                                         at->at_usage =
1642                                             LDAP_SCHEMA_DIRECTORY_OPERATION;
1643                                 else if ( !strcasecmp(sval,"distributedOperation") )
1644                                         at->at_usage =
1645                                             LDAP_SCHEMA_DISTRIBUTED_OPERATION;
1646                                 else if ( !strcasecmp(sval,"dSAOperation") )
1647                                         at->at_usage =
1648                                             LDAP_SCHEMA_DSA_OPERATION;
1649                                 else {
1650                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1651                                         *errp = ss;
1652                                         LDAP_FREE(sval);
1653                                         ldap_attributetype_free(at);
1654                                         return NULL;
1655                                 }
1656                                 LDAP_FREE(sval);
1657                                 parse_whsp(&ss);
1658                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1659                                 /* Should be parse_qdstrings */
1660                                 ext_vals = parse_qdescrs(&ss, code);
1661                                 if ( !ext_vals ) {
1662                                         *errp = ss;
1663                                         ldap_attributetype_free(at);
1664                                         return NULL;
1665                                 }
1666                                 if ( add_extension(&at->at_extensions,
1667                                                     sval, ext_vals) ) {
1668                                         *code = LDAP_SCHERR_OUTOFMEM;
1669                                         *errp = ss;
1670                                         LDAP_FREE(sval);
1671                                         ldap_attributetype_free(at);
1672                                         return NULL;
1673                                 }
1674                         } else {
1675                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1676                                 *errp = ss;
1677                                 LDAP_FREE(sval);
1678                                 ldap_attributetype_free(at);
1679                                 return NULL;
1680                         }
1681                         break;
1682                 default:
1683                         *code = LDAP_SCHERR_UNEXPTOKEN;
1684                         *errp = ss;
1685                         LDAP_FREE(sval);
1686                         ldap_attributetype_free(at);
1687                         return NULL;
1688                 }
1689         }
1690 }
1691
1692 void
1693 ldap_objectclass_free(LDAPObjectClass * oc)
1694 {
1695         LDAP_FREE(oc->oc_oid);
1696         LDAP_VFREE(oc->oc_names);
1697         LDAP_FREE(oc->oc_desc);
1698         LDAP_VFREE(oc->oc_sup_oids);
1699         LDAP_VFREE(oc->oc_at_oids_must);
1700         LDAP_VFREE(oc->oc_at_oids_may);
1701         free_extensions(oc->oc_extensions);
1702         LDAP_FREE(oc);
1703 }
1704
1705 LDAPObjectClass *
1706 ldap_str2objectclass( const char * s, int * code, const char ** errp, const int flags )
1707 {
1708         int kind;
1709         const char * ss = s;
1710         char * sval;
1711         int seen_name = 0;
1712         int seen_desc = 0;
1713         int seen_obsolete = 0;
1714         int seen_sup = 0;
1715         int seen_kind = 0;
1716         int seen_must = 0;
1717         int seen_may = 0;
1718         LDAPObjectClass * oc;
1719         char ** ext_vals;
1720         const char * savepos;
1721
1722         if ( !s ) {
1723                 *code = LDAP_SCHERR_EMPTY;
1724                 *errp = "";
1725                 return NULL;
1726         }
1727
1728         *errp = s;
1729         oc = LDAP_CALLOC(1,sizeof(LDAPObjectClass));
1730
1731         if ( !oc ) {
1732                 *code = LDAP_SCHERR_OUTOFMEM;
1733                 return NULL;
1734         }
1735
1736         kind = get_token(&ss,&sval);
1737         if ( kind != TK_LEFTPAREN ) {
1738                 *code = LDAP_SCHERR_NOLEFTPAREN;
1739                 LDAP_FREE(sval);
1740                 ldap_objectclass_free(oc);
1741                 return NULL;
1742         }
1743
1744         /*
1745          * Definitions MUST begin with an OID in the numericoid format.
1746          * However, this routine is used by clients to parse the response
1747          * from servers and very well known servers will provide an OID
1748          * in the wrong format or even no OID at all.  We do our best to
1749          * extract info from those servers.
1750          */
1751         parse_whsp(&ss);
1752         savepos = ss;
1753         oc->oc_oid = parse_numericoid(&ss,code,0);
1754         if ( !oc->oc_oid ) {
1755                 if ( flags & LDAP_SCHEMA_ALLOW_ALL ) {
1756                         /* Backtracking */
1757                         ss = savepos;
1758                         kind = get_token(&ss,&sval);
1759                         if ( kind == TK_BAREWORD ) {
1760                                 if ( !strcmp(sval, "NAME") ||
1761                                      !strcmp(sval, "DESC") ||
1762                                      !strcmp(sval, "OBSOLETE") ||
1763                                      !strcmp(sval, "SUP") ||
1764                                      !strcmp(sval, "ABSTRACT") ||
1765                                      !strcmp(sval, "STRUCTURAL") ||
1766                                      !strcmp(sval, "AUXILIARY") ||
1767                                      !strcmp(sval, "MUST") ||
1768                                      !strncmp(sval, "X-", 2) ) {
1769                                         /* Missing OID, backtrack */
1770                                         ss = savepos;
1771                                 } else {
1772                                         /* Non-numerical OID, ignore */
1773                                 }
1774                         }
1775                         LDAP_FREE(sval);
1776                 } else {
1777                         *errp = ss;
1778                         ldap_objectclass_free(oc);
1779                         return NULL;
1780                 }
1781         }
1782         parse_whsp(&ss);
1783
1784         /*
1785          * Beyond this point we will be liberal an accept the items
1786          * in any order.
1787          */
1788         while (1) {
1789                 kind = get_token(&ss,&sval);
1790                 switch (kind) {
1791                 case TK_EOS:
1792                         *code = LDAP_SCHERR_NORIGHTPAREN;
1793                         *errp = ss;
1794                         ldap_objectclass_free(oc);
1795                         return NULL;
1796                 case TK_RIGHTPAREN:
1797                         return oc;
1798                 case TK_BAREWORD:
1799                         if ( !strcmp(sval,"NAME") ) {
1800                                 LDAP_FREE(sval);
1801                                 if ( seen_name ) {
1802                                         *code = LDAP_SCHERR_DUPOPT;
1803                                         *errp = ss;
1804                                         ldap_objectclass_free(oc);
1805                                         return(NULL);
1806                                 }
1807                                 seen_name = 1;
1808                                 oc->oc_names = parse_qdescrs(&ss,code);
1809                                 if ( !oc->oc_names ) {
1810                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1811                                                 *code = LDAP_SCHERR_BADNAME;
1812                                         *errp = ss;
1813                                         ldap_objectclass_free(oc);
1814                                         return NULL;
1815                                 }
1816                         } else if ( !strcmp(sval,"DESC") ) {
1817                                 LDAP_FREE(sval);
1818                                 if ( seen_desc ) {
1819                                         *code = LDAP_SCHERR_DUPOPT;
1820                                         *errp = ss;
1821                                         ldap_objectclass_free(oc);
1822                                         return(NULL);
1823                                 }
1824                                 seen_desc = 1;
1825                                 parse_whsp(&ss);
1826                                 kind = get_token(&ss,&sval);
1827                                 if ( kind != TK_QDSTRING ) {
1828                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1829                                         *errp = ss;
1830                                         LDAP_FREE(sval);
1831                                         ldap_objectclass_free(oc);
1832                                         return NULL;
1833                                 }
1834                                 oc->oc_desc = sval;
1835                                 parse_whsp(&ss);
1836                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1837                                 LDAP_FREE(sval);
1838                                 if ( seen_obsolete ) {
1839                                         *code = LDAP_SCHERR_DUPOPT;
1840                                         *errp = ss;
1841                                         ldap_objectclass_free(oc);
1842                                         return(NULL);
1843                                 }
1844                                 seen_obsolete = 1;
1845                                 oc->oc_obsolete = LDAP_SCHEMA_YES;
1846                                 parse_whsp(&ss);
1847                         } else if ( !strcmp(sval,"SUP") ) {
1848                                 LDAP_FREE(sval);
1849                                 if ( seen_sup ) {
1850                                         *code = LDAP_SCHERR_DUPOPT;
1851                                         *errp = ss;
1852                                         ldap_objectclass_free(oc);
1853                                         return(NULL);
1854                                 }
1855                                 seen_sup = 1;
1856                                 oc->oc_sup_oids = parse_oids(&ss,
1857                                                              code,
1858                                                              flags);
1859                                 if ( !oc->oc_sup_oids ) {
1860                                         *errp = ss;
1861                                         ldap_objectclass_free(oc);
1862                                         return NULL;
1863                                 }
1864                         } else if ( !strcmp(sval,"ABSTRACT") ) {
1865                                 LDAP_FREE(sval);
1866                                 if ( seen_kind ) {
1867                                         *code = LDAP_SCHERR_DUPOPT;
1868                                         *errp = ss;
1869                                         ldap_objectclass_free(oc);
1870                                         return(NULL);
1871                                 }
1872                                 seen_kind = 1;
1873                                 oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
1874                                 parse_whsp(&ss);
1875                         } else if ( !strcmp(sval,"STRUCTURAL") ) {
1876                                 LDAP_FREE(sval);
1877                                 if ( seen_kind ) {
1878                                         *code = LDAP_SCHERR_DUPOPT;
1879                                         *errp = ss;
1880                                         ldap_objectclass_free(oc);
1881                                         return(NULL);
1882                                 }
1883                                 seen_kind = 1;
1884                                 oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
1885                                 parse_whsp(&ss);
1886                         } else if ( !strcmp(sval,"AUXILIARY") ) {
1887                                 LDAP_FREE(sval);
1888                                 if ( seen_kind ) {
1889                                         *code = LDAP_SCHERR_DUPOPT;
1890                                         *errp = ss;
1891                                         ldap_objectclass_free(oc);
1892                                         return(NULL);
1893                                 }
1894                                 seen_kind = 1;
1895                                 oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
1896                                 parse_whsp(&ss);
1897                         } else if ( !strcmp(sval,"MUST") ) {
1898                                 LDAP_FREE(sval);
1899                                 if ( seen_must ) {
1900                                         *code = LDAP_SCHERR_DUPOPT;
1901                                         *errp = ss;
1902                                         ldap_objectclass_free(oc);
1903                                         return(NULL);
1904                                 }
1905                                 seen_must = 1;
1906                                 oc->oc_at_oids_must = parse_oids(&ss,code,0);
1907                                 if ( !oc->oc_at_oids_must ) {
1908                                         *errp = ss;
1909                                         ldap_objectclass_free(oc);
1910                                         return NULL;
1911                                 }
1912                                 parse_whsp(&ss);
1913                         } else if ( !strcmp(sval,"MAY") ) {
1914                                 LDAP_FREE(sval);
1915                                 if ( seen_may ) {
1916                                         *code = LDAP_SCHERR_DUPOPT;
1917                                         *errp = ss;
1918                                         ldap_objectclass_free(oc);
1919                                         return(NULL);
1920                                 }
1921                                 seen_may = 1;
1922                                 oc->oc_at_oids_may = parse_oids(&ss,code,0);
1923                                 if ( !oc->oc_at_oids_may ) {
1924                                         *errp = ss;
1925                                         ldap_objectclass_free(oc);
1926                                         return NULL;
1927                                 }
1928                                 parse_whsp(&ss);
1929                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1930                                 /* Should be parse_qdstrings */
1931                                 ext_vals = parse_qdescrs(&ss, code);
1932                                 if ( !ext_vals ) {
1933                                         *errp = ss;
1934                                         ldap_objectclass_free(oc);
1935                                         return NULL;
1936                                 }
1937                                 if ( add_extension(&oc->oc_extensions,
1938                                                     sval, ext_vals) ) {
1939                                         *code = LDAP_SCHERR_OUTOFMEM;
1940                                         *errp = ss;
1941                                         LDAP_FREE(sval);
1942                                         ldap_objectclass_free(oc);
1943                                         return NULL;
1944                                 }
1945                         } else {
1946                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1947                                 *errp = ss;
1948                                 LDAP_FREE(sval);
1949                                 ldap_objectclass_free(oc);
1950                                 return NULL;
1951                         }
1952                         break;
1953                 default:
1954                         *code = LDAP_SCHERR_UNEXPTOKEN;
1955                         *errp = ss;
1956                         LDAP_FREE(sval);
1957                         ldap_objectclass_free(oc);
1958                         return NULL;
1959                 }
1960         }
1961 }
1962
1963 static char *const err2text[] = {
1964         "Success",
1965         "Out of memory",
1966         "Unexpected token",
1967         "Missing opening parenthesis",
1968         "Missing closing parenthesis",
1969         "Expecting digit",
1970         "Expecting a name",
1971         "Bad description",
1972         "Bad superiors",
1973         "Duplicate option",
1974         "Unexpected end of data"
1975 };
1976
1977 char *
1978 ldap_scherr2str(int code)
1979 {
1980         if ( code < 0 || code >= (sizeof(err2text)/sizeof(char *)) ) {
1981                 return "Unknown error";
1982         } else {
1983                 return err2text[code];
1984         }
1985 }