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