]> git.sur5r.net Git - openldap/blob - libraries/libldap/schema.c
Add ber_strdup(), remove ldap_int_strdup(), add LDAP_STRDUP macro
[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 <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_oids(ss,oc->oc_sup_oids);
298         }
299
300         switch (oc->oc_kind) {
301         case LDAP_SCHEMA_ABSTRACT:
302                 print_literal(ss,"ABSTRACT");
303                 break;
304         case LDAP_SCHEMA_STRUCTURAL:
305                 print_literal(ss,"STRUCTURAL");
306                 break;
307         case LDAP_SCHEMA_AUXILIARY:
308                 print_literal(ss,"AUXILIARY");
309                 break;
310         default:
311                 print_literal(ss,"KIND-UNKNOWN");
312                 break;
313         }
314         print_whsp(ss);
315         
316         if ( oc->oc_at_oids_must ) {
317                 print_literal(ss,"MUST");
318                 print_whsp(ss);
319                 print_oids(ss,oc->oc_at_oids_must);
320                 print_whsp(ss);
321         }
322
323         if ( oc->oc_at_oids_may ) {
324                 print_literal(ss,"MAY");
325                 print_whsp(ss);
326                 print_oids(ss,oc->oc_at_oids_may);
327                 print_whsp(ss);
328         }
329
330         print_whsp(ss);
331         print_literal(ss,")");
332
333         retstring = LDAP_STRDUP(safe_string_val(ss));
334         safe_string_free(ss);
335         return(retstring);
336 }
337
338 char *
339 ldap_attributetype2str( LDAP_ATTRIBUTE_TYPE * at )
340 {
341         safe_string * ss;
342         char * retstring;
343         
344         ss = new_safe_string(256);
345         if ( !ss )
346                 return NULL;
347
348         print_literal(ss,"(");
349         print_whsp(ss);
350
351         print_numericoid(ss, at->at_oid);
352         print_whsp(ss);
353
354         if ( at->at_names ) {
355                 print_literal(ss,"NAME");
356                 print_qdescrs(ss,at->at_names);
357         }
358
359         if ( at->at_desc ) {
360                 print_literal(ss,"DESC");
361                 print_qdstring(ss,at->at_desc);
362         }
363
364         if ( at->at_obsolete == LDAP_SCHEMA_YES ) {
365                 print_literal(ss, "OBSOLETE");
366                 print_whsp(ss);
367         }
368
369         if ( at->at_sup_oid ) {
370                 print_literal(ss,"SUP");
371                 print_woid(ss,at->at_sup_oid);
372         }
373
374         if ( at->at_equality_oid ) {
375                 print_literal(ss,"EQUALITY");
376                 print_woid(ss,at->at_equality_oid);
377         }
378
379         if ( at->at_ordering_oid ) {
380                 print_literal(ss,"ORDERING");
381                 print_woid(ss,at->at_ordering_oid);
382         }
383
384         if ( at->at_substr_oid ) {
385                 print_literal(ss,"SUBSTR");
386                 print_woid(ss,at->at_substr_oid);
387         }
388
389         if ( at->at_syntax_oid ) {
390                 print_literal(ss,"SYNTAX");
391                 print_whsp(ss);
392                 print_noidlen(ss,at->at_syntax_oid,at->at_syntax_len);
393         }
394
395         if ( at->at_single_value == LDAP_SCHEMA_YES ) {
396                 print_literal(ss,"SINGLE-VALUE");
397                 print_whsp(ss);
398         }
399
400         if ( at->at_collective == LDAP_SCHEMA_YES ) {
401                 print_literal(ss,"COLLECTIVE");
402                 print_whsp(ss);
403         }
404
405         if ( at->at_no_user_mod == LDAP_SCHEMA_YES ) {
406                 print_literal(ss,"NO-USER-MODIFICATION");
407                 print_whsp(ss);
408         }
409
410         if ( at->at_usage != LDAP_SCHEMA_USER_APPLICATIONS ) {
411                 print_literal(ss,"USAGE");
412                 print_whsp(ss);
413                 switch (at->at_usage) {
414                 case LDAP_SCHEMA_DIRECTORY_OPERATION:
415                         print_literal(ss,"directoryOperation");
416                         break;
417                 case LDAP_SCHEMA_DISTRIBUTED_OPERATION:
418                         print_literal(ss,"distributedOperation");
419                         break;
420                 case LDAP_SCHEMA_DSA_OPERATION:
421                         print_literal(ss,"dSAOperation");
422                         break;
423                 default:
424                         print_literal(ss,"UNKNOWN");
425                         break;
426                 }
427         }
428         
429         print_whsp(ss);
430         print_literal(ss,")");
431
432         retstring = LDAP_STRDUP(safe_string_val(ss));
433         safe_string_free(ss);
434         return(retstring);
435 }
436
437 /*
438  * Now come the parsers.  There is one parser for each entity type:
439  * objectclasses, attributetypes, etc.
440  *
441  * Each of them is written as a recursive-descent parser, except that
442  * none of them is really recursive.  But the idea is kept: there
443  * is one routine per non-terminal that eithers gobbles lexical tokens
444  * or calls lower-level routines, etc.
445  *
446  * The scanner is implemented in the routine get_token.  Actually,
447  * get_token is more than a scanner and will return tokens that are
448  * in fact non-terminals in the grammar.  So you can see the whole
449  * approach as the combination of a low-level bottom-up recognizer
450  * combined with a scanner and a number of top-down parsers.  Or just
451  * consider that the real grammars recognized by the parsers are not
452  * those of the standards.  As a matter of fact, our parsers are more
453  * liberal than the spec when there is no ambiguity.
454  *
455  * The difference is pretty academic (modulo bugs or incorrect
456  * interpretation of the specs).
457  */
458
459 #define TK_NOENDQUOTE   -2
460 #define TK_OUTOFMEM     -1
461 #define TK_EOS          0
462 #define TK_UNEXPCHAR    1
463 #define TK_BAREWORD     2
464 #define TK_QDSTRING     3
465 #define TK_LEFTPAREN    4
466 #define TK_RIGHTPAREN   5
467 #define TK_DOLLAR       6
468 #define TK_QDESCR       TK_QDSTRING
469
470 struct token {
471         int type;
472         char *sval;
473 };
474
475 static int
476 get_token(char ** sp, char ** token_val)
477 {
478         int kind;
479         char * p;
480         char * q;
481         char * res;
482
483         switch (**sp) {
484         case '\0':
485                 kind = TK_EOS;
486                 (*sp)++;
487                 break;
488         case '(':
489                 kind = TK_LEFTPAREN;
490                 (*sp)++;
491                 break;
492         case ')':
493                 kind = TK_RIGHTPAREN;
494                 (*sp)++;
495                 break;
496         case '$':
497                 kind = TK_DOLLAR;
498                 (*sp)++;
499                 break;
500         case '\'':
501                 kind = TK_QDSTRING;
502                 (*sp)++;
503                 p = *sp;
504                 while ( **sp != '\'' && **sp != '\0' )
505                         (*sp)++;
506                 if ( **sp == '\'' ) {
507                         q = *sp;
508                         res = LDAP_MALLOC(q-p+1);
509                         if ( !res ) {
510                                 kind = TK_OUTOFMEM;
511                         } else {
512                                 strncpy(res,p,q-p);
513                                 res[q-p] = '\0';
514                                 *token_val = res;
515                         }
516                         (*sp)++;
517                 } else {
518                         kind = TK_NOENDQUOTE;
519                 }
520                 break;
521         default:
522                 kind = TK_BAREWORD;
523                 p = *sp;
524                 while ( !isspace(**sp) && **sp != '\0' )
525                         (*sp)++;
526                 q = *sp;
527                 res = LDAP_MALLOC(q-p+1);
528                 if ( !res ) {
529                         kind = TK_OUTOFMEM;
530                 } else {
531                         strncpy(res,p,q-p);
532                         res[q-p] = '\0';
533                         *token_val = res;
534                 }
535                 break;
536 /*              kind = TK_UNEXPCHAR; */
537 /*              break; */
538         }
539         
540         return kind;
541 }
542
543 /* Gobble optional whitespace */
544 static void
545 parse_whsp(char **sp)
546 {
547         while (isspace(**sp))
548                 (*sp)++;
549 }
550
551 /* TBC:!!
552  * General note for all parsers: to guarantee the algorithm halts they
553  * must always advance the pointer even when an error is found.  For
554  * this one is not that important since an error here is fatal at the
555  * upper layers, but it is a simple strategy that will not get in
556  * endless loops.
557  */
558
559 /* Parse a sequence of dot-separated decimal strings */
560 static char *
561 parse_numericoid(char **sp, int *code)
562 {
563         char * res;
564         char * start = *sp;
565         int len;
566
567         /* Each iteration of this loops gets one decimal string */
568         while (**sp) {
569                 if ( !isdigit(**sp) ) {
570                         /*
571                          * Initial char is not a digit or char after dot is
572                          * not a digit
573                          */
574                         *code = LDAP_SCHERR_NODIGIT;
575                         return NULL;
576                 }
577                 (*sp)++;
578                 while ( isdigit(**sp) )
579                         (*sp)++;
580                 if ( **sp != '.' )
581                         break;
582                 /* Otherwise, gobble the dot and loop again */
583                 (*sp)++;
584         }
585         /* Now *sp points at the char past the numericoid. Perfect. */
586         len = *sp - start;
587         res = LDAP_MALLOC(len+1);
588         if (!res) {
589           *code = LDAP_SCHERR_OUTOFMEM;
590           return(NULL);
591         }
592         strncpy(res,start,len);
593         res[len] = '\0';
594         return(res);
595 }
596
597 /* Parse a qdescr or a list of them enclosed in () */
598 static char **
599 parse_qdescrs(char **sp, int *code)
600 {
601         char ** res;
602         char ** res1;
603         int kind;
604         char * sval;
605         int size;
606         int pos;
607
608         parse_whsp(sp);
609         kind = get_token(sp,&sval);
610         if ( kind == TK_LEFTPAREN ) {
611                 /* Let's presume there will be at least 2 entries */
612                 size = 3;
613                 res = LDAP_CALLOC(3,sizeof(char *));
614                 if ( !res ) {
615                         *code = LDAP_SCHERR_OUTOFMEM;
616                         return NULL;
617                 }
618                 pos = 0;
619                 while (1) {
620                         parse_whsp(sp);
621                         kind = get_token(sp,&sval);
622                         if ( kind == TK_RIGHTPAREN )
623                                 break;
624                         if ( kind == TK_QDESCR ) {
625                                 if ( pos == size-2 ) {
626                                         size++;
627                                         res1 = LDAP_REALLOC(res,size*sizeof(char *));
628                                         if ( !res1 ) {
629                                                 LDAP_VFREE(res);
630                                                 *code = LDAP_SCHERR_OUTOFMEM;
631                                                 return(NULL);
632                                         }
633                                         res = res1;
634                                 }
635                                 res[pos] = sval;
636                                 pos++;
637                                 parse_whsp(sp);
638                         } else {
639                                 LDAP_VFREE(res);
640                                 *code = LDAP_SCHERR_UNEXPTOKEN;
641                                 return(NULL);
642                         }
643                 }
644                 res[pos] = NULL;
645                 parse_whsp(sp);
646                 return(res);
647         } else if ( kind == TK_QDESCR ) {
648                 res = LDAP_CALLOC(2,sizeof(char *));
649                 if ( !res ) {
650                         *code = LDAP_SCHERR_OUTOFMEM;
651                         return NULL;
652                 }
653                 res[0] = sval;
654                 res[1] = NULL;
655                 parse_whsp(sp);
656                 return res;
657         } else {
658                 *code = LDAP_SCHERR_BADNAME;
659                 return NULL;
660         }
661 }
662
663 /* Parse a woid */
664 static char *
665 parse_woid(char **sp, int *code)
666 {
667         char * sval;
668         int kind;
669
670         parse_whsp(sp);
671         kind = get_token(sp, &sval);
672         if ( kind != TK_BAREWORD ) {
673                 *code = LDAP_SCHERR_UNEXPTOKEN;
674                 return NULL;
675         }
676         parse_whsp(sp);
677         return sval;
678 }
679
680 /* Parse a noidlen */
681 static char *
682 parse_noidlen(char **sp, int *code, int *len)
683 {
684         char * sval;
685         int kind;
686
687         *len = 0;
688         kind = get_token(sp, &sval);
689         if ( kind != TK_BAREWORD ) {
690                 *code = LDAP_SCHERR_UNEXPTOKEN;
691                 return NULL;
692         }
693         if ( **sp == '{' ) {
694                 (*sp)++;
695                 *len = atoi(*sp);
696                 while ( isdigit(**sp) )
697                         (*sp)++;
698                 (*sp)++;
699                 if ( **sp != '}' ) {
700                         *code = LDAP_SCHERR_UNEXPTOKEN;
701                         LDAP_FREE(sval);
702                         return NULL;
703                 }
704                 (*sp)++;
705         }               
706         return sval;
707 }
708
709 /*
710  * Next routine will accept a qdstring in place of an oid.  This is
711  * necessary to interoperate with Netscape Directory server that
712  * will improperly quote each oid (at least those of the descr kind)
713  * in the SUP clause.
714  */
715
716 /* Parse a woid or a $-separated list of them enclosed in () */
717 static char **
718 parse_oids(char **sp, int *code)
719 {
720         char ** res;
721         char ** res1;
722         int kind;
723         char * sval;
724         int size;
725         int pos;
726
727         /*
728          * Strictly speaking, doing this here accepts whsp before the
729          * ( at the begining of an oidlist, but his is harmless.  Also,
730          * we are very liberal in what we accept as an OID.  Maybe
731          * refine later.
732          */
733         parse_whsp(sp);
734         kind = get_token(sp,&sval);
735         if ( kind == TK_LEFTPAREN ) {
736                 /* Let's presume there will be at least 2 entries */
737                 size = 3;
738                 res = LDAP_CALLOC(3,sizeof(char *));
739                 if ( !res ) {
740                         *code = LDAP_SCHERR_OUTOFMEM;
741                         return NULL;
742                 }
743                 pos = 0;
744                 parse_whsp(sp);
745                 kind = get_token(sp,&sval);
746                 if ( kind == TK_BAREWORD || kind == TK_QDSTRING ) {
747                         res[pos] = sval;
748                         pos++;
749                 } else {
750                         *code = LDAP_SCHERR_UNEXPTOKEN;
751                         LDAP_VFREE(res);
752                         return NULL;
753                 }
754                 parse_whsp(sp);
755                 while (1) {
756                         kind = get_token(sp,&sval);
757                         if ( kind == TK_RIGHTPAREN )
758                                 break;
759                         if ( kind == TK_DOLLAR ) {
760                                 parse_whsp(sp);
761                                 kind = get_token(sp,&sval);
762                                 if ( kind == TK_BAREWORD ||
763                                      kind == TK_QDSTRING ) {
764                                         if ( pos == size-2 ) {
765                                                 size++;
766                                                 res1 = LDAP_REALLOC(res,size*sizeof(char *));
767                                                 if ( !res1 ) {
768                                                   LDAP_VFREE(res);
769                                                   *code = LDAP_SCHERR_OUTOFMEM;
770                                                   return(NULL);
771                                                 }
772                                                 res = res1;
773                                         }
774                                         res[pos] = sval;
775                                         pos++;
776                                 } else {
777                                         *code = LDAP_SCHERR_UNEXPTOKEN;
778                                         LDAP_VFREE(res);
779                                         return NULL;
780                                 }
781                                 parse_whsp(sp);
782                         } else {
783                                 *code = LDAP_SCHERR_UNEXPTOKEN;
784                                 LDAP_VFREE(res);
785                                 return NULL;
786                         }
787                 }
788                 res[pos] = NULL;
789                 parse_whsp(sp);
790                 return(res);
791         } else if ( kind == TK_BAREWORD || kind == TK_QDSTRING ) {
792                 res = LDAP_CALLOC(2,sizeof(char *));
793                 if ( !res ) {
794                         *code = LDAP_SCHERR_OUTOFMEM;
795                         return NULL;
796                 }
797                 res[0] = sval;
798                 res[1] = NULL;
799                 parse_whsp(sp);
800                 return res;
801         } else {
802                 *code = LDAP_SCHERR_BADNAME;
803                 return NULL;
804         }
805 }
806
807 static void
808 free_syn(LDAP_SYNTAX * syn)
809 {
810         LDAP_FREE(syn->syn_oid);
811         LDAP_FREE(syn->syn_desc);
812         LDAP_FREE(syn);
813 }
814
815 LDAP_SYNTAX *
816 ldap_str2syntax( char * s, int * code, char ** errp )
817 {
818         int kind;
819         char * ss = s;
820         char * sval;
821         int seen_desc = 0;
822         LDAP_SYNTAX * syn;
823         char ** ssdummy;
824
825         if ( !s ) {
826                 *code = LDAP_SCHERR_EMPTY;
827                 *errp = "";
828                 return NULL;
829         }
830
831         *errp = s;
832         syn = LDAP_CALLOC(1,sizeof(LDAP_SYNTAX));
833
834         if ( !syn ) {
835                 *code = LDAP_SCHERR_OUTOFMEM;
836                 return NULL;
837         }
838
839         kind = get_token(&ss,&sval);
840         if ( kind != TK_LEFTPAREN ) {
841                 *code = LDAP_SCHERR_NOLEFTPAREN;
842                 free_syn(syn);
843                 return NULL;
844         }
845
846         parse_whsp(&ss);
847         syn->syn_oid = parse_numericoid(&ss,code);
848         if ( !syn->syn_oid ) {
849                 *errp = ss;
850                 free_syn(syn);
851                 return NULL;
852         }
853         parse_whsp(&ss);
854
855         /*
856          * Beyond this point we will be liberal and accept the items
857          * in any order.
858          */
859         while (1) {
860                 kind = get_token(&ss,&sval);
861                 switch (kind) {
862                 case TK_EOS:
863                         *code = LDAP_SCHERR_NORIGHTPAREN;
864                         *errp = ss;
865                         free_syn(syn);
866                         return NULL;
867                 case TK_RIGHTPAREN:
868                         return syn;
869                 case TK_BAREWORD:
870                         if ( !strcmp(sval,"DESC") ) {
871                                 if ( seen_desc ) {
872                                         *code = LDAP_SCHERR_DUPOPT;
873                                         *errp = ss;
874                                         free_syn(syn);
875                                         return(NULL);
876                                 }
877                                 seen_desc = 1;
878                                 parse_whsp(&ss);
879                                 kind = get_token(&ss,&sval);
880                                 if ( kind != TK_QDSTRING ) {
881                                         *code = LDAP_SCHERR_UNEXPTOKEN;
882                                         *errp = ss;
883                                         free_syn(syn);
884                                         return NULL;
885                                 }
886                                 syn->syn_desc = sval;
887                                 parse_whsp(&ss);
888                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
889                                 /* Should be parse_qdstrings */
890                                 ssdummy = parse_qdescrs(&ss, code);
891                                 if ( !ssdummy ) {
892                                         *errp = ss;
893                                         free_syn(syn);
894                                         return NULL;
895                                 }
896                         } else {
897                                 *code = LDAP_SCHERR_UNEXPTOKEN;
898                                 *errp = ss;
899                                 free_syn(syn);
900                                 return NULL;
901                         }
902                         break;
903                 default:
904                         *code = LDAP_SCHERR_UNEXPTOKEN;
905                         *errp = ss;
906                         free_syn(syn);
907                         return NULL;
908                 }
909         }
910 }
911
912 static void
913 free_at(LDAP_ATTRIBUTE_TYPE * at)
914 {
915         LDAP_FREE(at->at_oid);
916         LDAP_VFREE(at->at_names);
917         LDAP_FREE(at->at_desc);
918         LDAP_FREE(at->at_sup_oid);
919         LDAP_FREE(at->at_equality_oid);
920         LDAP_FREE(at->at_ordering_oid);
921         LDAP_FREE(at->at_substr_oid);
922         LDAP_FREE(at->at_syntax_oid);
923         LDAP_FREE(at);
924 }
925
926 LDAP_ATTRIBUTE_TYPE *
927 ldap_str2attributetype( char * s, int * code, char ** errp )
928 {
929         int kind;
930         char * ss = s;
931         char * sval;
932         int seen_name = 0;
933         int seen_desc = 0;
934         int seen_obsolete = 0;
935         int seen_sup = 0;
936         int seen_equality = 0;
937         int seen_ordering = 0;
938         int seen_substr = 0;
939         int seen_syntax = 0;
940         int seen_usage = 0;
941         int seen_kind = 0;
942         int seen_must = 0;
943         int seen_may = 0;
944         LDAP_ATTRIBUTE_TYPE * at;
945         char ** ssdummy;
946
947         if ( !s ) {
948                 *code = LDAP_SCHERR_EMPTY;
949                 *errp = "";
950                 return NULL;
951         }
952
953         *errp = s;
954         at = LDAP_CALLOC(1,sizeof(LDAP_ATTRIBUTE_TYPE));
955
956         if ( !at ) {
957                 *code = LDAP_SCHERR_OUTOFMEM;
958                 return NULL;
959         }
960
961         kind = get_token(&ss,&sval);
962         if ( kind != TK_LEFTPAREN ) {
963                 *code = LDAP_SCHERR_NOLEFTPAREN;
964                 free_at(at);
965                 return NULL;
966         }
967
968         parse_whsp(&ss);
969         at->at_oid = parse_numericoid(&ss,code);
970         if ( !at->at_oid ) {
971                 *errp = ss;
972                 free_at(at);
973                 return NULL;
974         }
975         parse_whsp(&ss);
976
977         /*
978          * Beyond this point we will be liberal and accept the items
979          * in any order.
980          */
981         while (1) {
982                 kind = get_token(&ss,&sval);
983                 switch (kind) {
984                 case TK_EOS:
985                         *code = LDAP_SCHERR_NORIGHTPAREN;
986                         *errp = ss;
987                         free_at(at);
988                         return NULL;
989                 case TK_RIGHTPAREN:
990                         return at;
991                 case TK_BAREWORD:
992                         if ( !strcmp(sval,"NAME") ) {
993                                 if ( seen_name ) {
994                                         *code = LDAP_SCHERR_DUPOPT;
995                                         *errp = ss;
996                                         free_at(at);
997                                         return(NULL);
998                                 }
999                                 seen_name = 1;
1000                                 at->at_names = parse_qdescrs(&ss,code);
1001                                 if ( !at->at_names ) {
1002                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1003                                                 *code = LDAP_SCHERR_BADNAME;
1004                                         *errp = ss;
1005                                         free_at(at);
1006                                         return NULL;
1007                                 }
1008                         } else if ( !strcmp(sval,"DESC") ) {
1009                                 if ( seen_desc ) {
1010                                         *code = LDAP_SCHERR_DUPOPT;
1011                                         *errp = ss;
1012                                         free_at(at);
1013                                         return(NULL);
1014                                 }
1015                                 seen_desc = 1;
1016                                 parse_whsp(&ss);
1017                                 kind = get_token(&ss,&sval);
1018                                 if ( kind != TK_QDSTRING ) {
1019                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1020                                         *errp = ss;
1021                                         free_at(at);
1022                                         return NULL;
1023                                 }
1024                                 at->at_desc = sval;
1025                                 parse_whsp(&ss);
1026                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1027                                 if ( seen_obsolete ) {
1028                                         *code = LDAP_SCHERR_DUPOPT;
1029                                         *errp = ss;
1030                                         free_at(at);
1031                                         return(NULL);
1032                                 }
1033                                 seen_obsolete = 1;
1034                                 at->at_obsolete = LDAP_SCHEMA_YES;
1035                                 parse_whsp(&ss);
1036                         } else if ( !strcmp(sval,"SUP") ) {
1037                                 if ( seen_sup ) {
1038                                         *code = LDAP_SCHERR_DUPOPT;
1039                                         *errp = ss;
1040                                         free_at(at);
1041                                         return(NULL);
1042                                 }
1043                                 seen_sup = 1;
1044                                 at->at_sup_oid = parse_woid(&ss,code);
1045                                 if ( !at->at_sup_oid ) {
1046                                         *errp = ss;
1047                                         free_at(at);
1048                                         return NULL;
1049                                 }
1050                         } else if ( !strcmp(sval,"EQUALITY") ) {
1051                                 if ( seen_equality ) {
1052                                         *code = LDAP_SCHERR_DUPOPT;
1053                                         *errp = ss;
1054                                         free_at(at);
1055                                         return(NULL);
1056                                 }
1057                                 seen_equality = 1;
1058                                 at->at_equality_oid = parse_woid(&ss,code);
1059                                 if ( !at->at_equality_oid ) {
1060                                         *errp = ss;
1061                                         free_at(at);
1062                                         return NULL;
1063                                 }
1064                         } else if ( !strcmp(sval,"ORDERING") ) {
1065                                 if ( seen_ordering ) {
1066                                         *code = LDAP_SCHERR_DUPOPT;
1067                                         *errp = ss;
1068                                         free_at(at);
1069                                         return(NULL);
1070                                 }
1071                                 seen_ordering = 1;
1072                                 at->at_ordering_oid = parse_woid(&ss,code);
1073                                 if ( !at->at_ordering_oid ) {
1074                                         *errp = ss;
1075                                         free_at(at);
1076                                         return NULL;
1077                                 }
1078                         } else if ( !strcmp(sval,"SUBSTR") ) {
1079                                 if ( seen_substr ) {
1080                                         *code = LDAP_SCHERR_DUPOPT;
1081                                         *errp = ss;
1082                                         free_at(at);
1083                                         return(NULL);
1084                                 }
1085                                 seen_substr = 1;
1086                                 at->at_substr_oid = parse_woid(&ss,code);
1087                                 if ( !at->at_substr_oid ) {
1088                                         *errp = ss;
1089                                         free_at(at);
1090                                         return NULL;
1091                                 }
1092                         } else if ( !strcmp(sval,"SYNTAX") ) {
1093                                 if ( seen_syntax ) {
1094                                         *code = LDAP_SCHERR_DUPOPT;
1095                                         *errp = ss;
1096                                         free_at(at);
1097                                         return(NULL);
1098                                 }
1099                                 seen_syntax = 1;
1100                                 parse_whsp(&ss);
1101                                 at->at_syntax_oid = parse_noidlen(&ss,code,&at->at_syntax_len);
1102                                 if ( !at->at_syntax_oid ) {
1103                                         *errp = ss;
1104                                         free_at(at);
1105                                         return NULL;
1106                                 }
1107                                 parse_whsp(&ss);
1108                         } else if ( !strcmp(sval,"SINGLE-VALUE") ) {
1109                                 if ( at->at_single_value ) {
1110                                         *code = LDAP_SCHERR_DUPOPT;
1111                                         *errp = ss;
1112                                         free_at(at);
1113                                         return(NULL);
1114                                 }
1115                                 at->at_single_value = LDAP_SCHEMA_YES;
1116                                 parse_whsp(&ss);
1117                         } else if ( !strcmp(sval,"COLLECTIVE") ) {
1118                                 if ( at->at_collective ) {
1119                                         *code = LDAP_SCHERR_DUPOPT;
1120                                         *errp = ss;
1121                                         free_at(at);
1122                                         return(NULL);
1123                                 }
1124                                 at->at_collective = LDAP_SCHEMA_YES;
1125                                 parse_whsp(&ss);
1126                         } else if ( !strcmp(sval,"NO-USER-MODIFICATION") ) {
1127                                 if ( at->at_no_user_mod ) {
1128                                         *code = LDAP_SCHERR_DUPOPT;
1129                                         *errp = ss;
1130                                         free_at(at);
1131                                         return(NULL);
1132                                 }
1133                                 at->at_no_user_mod = LDAP_SCHEMA_YES;
1134                                 parse_whsp(&ss);
1135                         } else if ( !strcmp(sval,"USAGE") ) {
1136                                 if ( seen_usage ) {
1137                                         *code = LDAP_SCHERR_DUPOPT;
1138                                         *errp = ss;
1139                                         free_at(at);
1140                                         return(NULL);
1141                                 }
1142                                 seen_usage = 1;
1143                                 parse_whsp(&ss);
1144                                 kind = get_token(&ss,&sval);
1145                                 if ( kind != TK_BAREWORD ) {
1146                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1147                                         *errp = ss;
1148                                         free_at(at);
1149                                         return NULL;
1150                                 }
1151                                 if ( !strcasecmp(sval,"userApplications") )
1152                                         at->at_usage =
1153                                             LDAP_SCHEMA_USER_APPLICATIONS;
1154                                 else if ( !strcasecmp(sval,"directoryOperation") )
1155                                         at->at_usage =
1156                                             LDAP_SCHEMA_DIRECTORY_OPERATION;
1157                                 else if ( !strcasecmp(sval,"distributedOperation") )
1158                                         at->at_usage =
1159                                             LDAP_SCHEMA_DISTRIBUTED_OPERATION;
1160                                 else if ( !strcasecmp(sval,"dSAOperation") )
1161                                         at->at_usage =
1162                                             LDAP_SCHEMA_DSA_OPERATION;
1163                                 else {
1164                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1165                                         *errp = ss;
1166                                         free_at(at);
1167                                         return NULL;
1168                                 }
1169                                 parse_whsp(&ss);
1170                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1171                                 /* Should be parse_qdstrings */
1172                                 ssdummy = parse_qdescrs(&ss, code);
1173                                 if ( !ssdummy ) {
1174                                         *errp = ss;
1175                                         free_at(at);
1176                                         return NULL;
1177                                 }
1178                         } else {
1179                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1180                                 *errp = ss;
1181                                 free_at(at);
1182                                 return NULL;
1183                         }
1184                         break;
1185                 default:
1186                         *code = LDAP_SCHERR_UNEXPTOKEN;
1187                         *errp = ss;
1188                         free_at(at);
1189                         return NULL;
1190                 }
1191         }
1192 }
1193
1194 static void
1195 free_oc(LDAP_OBJECT_CLASS * oc)
1196 {
1197         LDAP_FREE(oc->oc_oid);
1198         LDAP_VFREE(oc->oc_names);
1199         LDAP_FREE(oc->oc_desc);
1200         LDAP_VFREE(oc->oc_sup_oids);
1201         LDAP_VFREE(oc->oc_at_oids_must);
1202         LDAP_VFREE(oc->oc_at_oids_may);
1203         LDAP_FREE(oc);
1204 }
1205
1206 LDAP_OBJECT_CLASS *
1207 ldap_str2objectclass( char * s, int * code, char ** errp )
1208 {
1209         int kind;
1210         char * ss = s;
1211         char * sval;
1212         int seen_name = 0;
1213         int seen_desc = 0;
1214         int seen_obsolete = 0;
1215         int seen_sup = 0;
1216         int seen_kind = 0;
1217         int seen_must = 0;
1218         int seen_may = 0;
1219         LDAP_OBJECT_CLASS * oc;
1220         char ** ssdummy;
1221
1222         if ( !s ) {
1223                 *code = LDAP_SCHERR_EMPTY;
1224                 *errp = "";
1225                 return NULL;
1226         }
1227
1228         *errp = s;
1229         oc = LDAP_CALLOC(1,sizeof(LDAP_OBJECT_CLASS));
1230
1231         if ( !oc ) {
1232                 *code = LDAP_SCHERR_OUTOFMEM;
1233                 return NULL;
1234         }
1235
1236         kind = get_token(&ss,&sval);
1237         if ( kind != TK_LEFTPAREN ) {
1238                 *code = LDAP_SCHERR_NOLEFTPAREN;
1239                 free_oc(oc);
1240                 return NULL;
1241         }
1242
1243         parse_whsp(&ss);
1244         oc->oc_oid = parse_numericoid(&ss,code);
1245         if ( !oc->oc_oid ) {
1246                 *errp = ss;
1247                 free_oc(oc);
1248                 return NULL;
1249         }
1250         parse_whsp(&ss);
1251
1252         /*
1253          * Beyond this point we will be liberal an accept the items
1254          * in any order.
1255          */
1256         while (1) {
1257                 kind = get_token(&ss,&sval);
1258                 switch (kind) {
1259                 case TK_EOS:
1260                         *code = LDAP_SCHERR_NORIGHTPAREN;
1261                         *errp = ss;
1262                         free_oc(oc);
1263                         return NULL;
1264                 case TK_RIGHTPAREN:
1265                         return oc;
1266                 case TK_BAREWORD:
1267                         if ( !strcmp(sval,"NAME") ) {
1268                                 if ( seen_name ) {
1269                                         *code = LDAP_SCHERR_DUPOPT;
1270                                         *errp = ss;
1271                                         free_oc(oc);
1272                                         return(NULL);
1273                                 }
1274                                 seen_name = 1;
1275                                 oc->oc_names = parse_qdescrs(&ss,code);
1276                                 if ( !oc->oc_names ) {
1277                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1278                                                 *code = LDAP_SCHERR_BADNAME;
1279                                         *errp = ss;
1280                                         free_oc(oc);
1281                                         return NULL;
1282                                 }
1283                         } else if ( !strcmp(sval,"DESC") ) {
1284                                 if ( seen_desc ) {
1285                                         *code = LDAP_SCHERR_DUPOPT;
1286                                         *errp = ss;
1287                                         free_oc(oc);
1288                                         return(NULL);
1289                                 }
1290                                 seen_desc = 1;
1291                                 parse_whsp(&ss);
1292                                 kind = get_token(&ss,&sval);
1293                                 if ( kind != TK_QDSTRING ) {
1294                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1295                                         *errp = ss;
1296                                         free_oc(oc);
1297                                         return NULL;
1298                                 }
1299                                 oc->oc_desc = sval;
1300                                 parse_whsp(&ss);
1301                         } else if ( !strcmp(sval,"OBSOLETE") ) {
1302                                 if ( seen_obsolete ) {
1303                                         *code = LDAP_SCHERR_DUPOPT;
1304                                         *errp = ss;
1305                                         free_oc(oc);
1306                                         return(NULL);
1307                                 }
1308                                 seen_obsolete = 1;
1309                                 oc->oc_obsolete = LDAP_SCHEMA_YES;
1310                                 parse_whsp(&ss);
1311                         } else if ( !strcmp(sval,"SUP") ) {
1312                                 if ( seen_sup ) {
1313                                         *code = LDAP_SCHERR_DUPOPT;
1314                                         *errp = ss;
1315                                         free_oc(oc);
1316                                         return(NULL);
1317                                 }
1318                                 seen_sup = 1;
1319                                 oc->oc_sup_oids = parse_oids(&ss,code);
1320                                 if ( !oc->oc_sup_oids ) {
1321                                         *errp = ss;
1322                                         free_oc(oc);
1323                                         return NULL;
1324                                 }
1325                         } else if ( !strcmp(sval,"ABSTRACT") ) {
1326                                 if ( seen_kind ) {
1327                                         *code = LDAP_SCHERR_DUPOPT;
1328                                         *errp = ss;
1329                                         free_oc(oc);
1330                                         return(NULL);
1331                                 }
1332                                 seen_kind = 1;
1333                                 oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
1334                                 parse_whsp(&ss);
1335                         } else if ( !strcmp(sval,"STRUCTURAL") ) {
1336                                 if ( seen_kind ) {
1337                                         *code = LDAP_SCHERR_DUPOPT;
1338                                         *errp = ss;
1339                                         free_oc(oc);
1340                                         return(NULL);
1341                                 }
1342                                 seen_kind = 1;
1343                                 oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
1344                                 parse_whsp(&ss);
1345                         } else if ( !strcmp(sval,"AUXILIARY") ) {
1346                                 if ( seen_kind ) {
1347                                         *code = LDAP_SCHERR_DUPOPT;
1348                                         *errp = ss;
1349                                         free_oc(oc);
1350                                         return(NULL);
1351                                 }
1352                                 seen_kind = 1;
1353                                 oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
1354                                 parse_whsp(&ss);
1355                         } else if ( !strcmp(sval,"MUST") ) {
1356                                 if ( seen_must ) {
1357                                         *code = LDAP_SCHERR_DUPOPT;
1358                                         *errp = ss;
1359                                         free_oc(oc);
1360                                         return(NULL);
1361                                 }
1362                                 seen_must = 1;
1363                                 oc->oc_at_oids_must = parse_oids(&ss,code);
1364                                 if ( !oc->oc_at_oids_must ) {
1365                                         *errp = ss;
1366                                         free_oc(oc);
1367                                         return NULL;
1368                                 }
1369                                 parse_whsp(&ss);
1370                         } else if ( !strcmp(sval,"MAY") ) {
1371                                 if ( seen_may ) {
1372                                         *code = LDAP_SCHERR_DUPOPT;
1373                                         *errp = ss;
1374                                         free_oc(oc);
1375                                         return(NULL);
1376                                 }
1377                                 seen_may = 1;
1378                                 oc->oc_at_oids_may = parse_oids(&ss,code);
1379                                 if ( !oc->oc_at_oids_may ) {
1380                                         *errp = ss;
1381                                         free_oc(oc);
1382                                         return NULL;
1383                                 }
1384                                 parse_whsp(&ss);
1385                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1386                                 /* Should be parse_qdstrings */
1387                                 ssdummy = parse_qdescrs(&ss, code);
1388                                 if ( !ssdummy ) {
1389                                         *errp = ss;
1390                                         free_oc(oc);
1391                                         return NULL;
1392                                 }
1393                         } else {
1394                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1395                                 *errp = ss;
1396                                 free_oc(oc);
1397                                 return NULL;
1398                         }
1399                         break;
1400                 default:
1401                         *code = LDAP_SCHERR_UNEXPTOKEN;
1402                         *errp = ss;
1403                         free_oc(oc);
1404                         return NULL;
1405                 }
1406         }
1407 }
1408
1409 static char *err2text[] = {
1410         "",
1411         "Out of memory",
1412         "Unexpected token",
1413         "Missing opening parenthesis",
1414         "Missing closing parenthesis",
1415         "Expecting digit",
1416         "Expecting a name",
1417         "Bad description",
1418         "Bad superiors",
1419         "Duplicate option",
1420         "Unexpected end of data"
1421 };
1422
1423 char *
1424 ldap_scherr2str(int code)
1425 {
1426         if ( code < 1 || code >= (sizeof(err2text)/sizeof(char *)) ) {
1427                 return "Unknown error";
1428         } else {
1429                 return err2text[code];
1430         }
1431 }