]> git.sur5r.net Git - openldap/blob - libraries/libldap/schema.c
plug yet another one-time leak
[openldap] / libraries / libldap / schema.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2005 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15
16 /*
17  * schema.c:  parsing routines used by servers and clients to process
18  *      schema definitions
19  */
20
21 #include "portable.h"
22
23 #include <stdio.h>
24 #include <ac/stdlib.h>
25
26 #include <ac/string.h>
27 #include <ac/time.h>
28
29 #include "ldap-int.h"
30
31 #include <ldap_schema.h>
32
33 static const char *
34 choose_name( char *names[], const char *fallback )
35 {
36         return (names != NULL && names[0] != NULL) ? names[0] : fallback;
37 }
38
39 LDAP_CONST char *
40 ldap_syntax2name( LDAPSyntax * syn )
41 {
42         return( syn->syn_oid );
43 }
44
45 LDAP_CONST char *
46 ldap_matchingrule2name( LDAPMatchingRule * mr )
47 {
48         return( choose_name( mr->mr_names, mr->mr_oid ) );
49 }
50
51 LDAP_CONST char *
52 ldap_matchingruleuse2name( LDAPMatchingRuleUse * mru )
53 {
54         return( choose_name( mru->mru_names, mru->mru_oid ) );
55 }
56
57 LDAP_CONST char *
58 ldap_attributetype2name( LDAPAttributeType * at )
59 {
60         return( choose_name( at->at_names, at->at_oid ) );
61 }
62
63 LDAP_CONST char *
64 ldap_objectclass2name( LDAPObjectClass * oc )
65 {
66         return( choose_name( oc->oc_names, oc->oc_oid ) );
67 }
68
69 LDAP_CONST char *
70 ldap_contentrule2name( LDAPContentRule * cr )
71 {
72         return( choose_name( cr->cr_names, cr->cr_oid ) );
73 }
74
75 LDAP_CONST char *
76 ldap_nameform2name( LDAPNameForm * nf )
77 {
78         return( choose_name( nf->nf_names, nf->nf_oid ) );
79 }
80
81 LDAP_CONST char *
82 ldap_structurerule2name( LDAPStructureRule * sr )
83 {
84         return( choose_name( sr->sr_names, NULL ) );
85 }
86
87 /*
88  * When pretty printing the entities we will be appending to a buffer.
89  * Since checking for overflow, realloc'ing and checking if no error
90  * is extremely boring, we will use a protection layer that will let
91  * us blissfully ignore the error until the end.  This layer is
92  * implemented with the help of the next type.
93  */
94
95 typedef struct safe_string {
96         char * val;
97         ber_len_t size;
98         ber_len_t pos;
99         int at_whsp;
100 } safe_string;
101
102 static safe_string *
103 new_safe_string(int size)
104 {
105         safe_string * ss;
106         
107         ss = LDAP_MALLOC(sizeof(safe_string));
108         if ( !ss )
109                 return(NULL);
110
111         ss->val = LDAP_MALLOC(size);
112         if ( !ss->val ) {
113                 LDAP_FREE(ss);
114                 return(NULL);
115         }
116
117         ss->size = size;
118         ss->pos = 0;
119         ss->at_whsp = 0;
120
121         return ss;
122 }
123
124 static void
125 safe_string_free(safe_string * ss)
126 {
127         if ( !ss )
128                 return;
129         LDAP_FREE(ss->val);
130         LDAP_FREE(ss);
131 }
132
133 #if 0   /* unused */
134 static char *
135 safe_string_val(safe_string * ss)
136 {
137         ss->val[ss->pos] = '\0';
138         return(ss->val);
139 }
140 #endif
141
142 static char *
143 safe_strdup(safe_string * ss)
144 {
145         char *ret = LDAP_MALLOC(ss->pos+1);
146         if (!ret)
147                 return NULL;
148         AC_MEMCPY(ret, ss->val, ss->pos);
149         ret[ss->pos] = '\0';
150         return ret;
151 }
152
153 static int
154 append_to_safe_string(safe_string * ss, char * s)
155 {
156         int l = strlen(s);
157         char * temp;
158
159         /*
160          * Some runaway process is trying to append to a string that
161          * overflowed and we could not extend.
162          */
163         if ( !ss->val )
164                 return -1;
165
166         /* We always make sure there is at least one position available */
167         if ( ss->pos + l >= ss->size-1 ) {
168                 ss->size *= 2;
169                 if ( ss->pos + l >= ss->size-1 ) {
170                         ss->size = ss->pos + l + 1;
171                 }
172
173                 temp = LDAP_REALLOC(ss->val, ss->size);
174                 if ( !temp ) {
175                         /* Trouble, out of memory */
176                         LDAP_FREE(ss->val);
177                         return -1;
178                 }
179                 ss->val = temp;
180         }
181         strncpy(&ss->val[ss->pos], s, l);
182         ss->pos += l;
183         if ( ss->pos > 0 && LDAP_SPACE(ss->val[ss->pos-1]) )
184                 ss->at_whsp = 1;
185         else
186                 ss->at_whsp = 0;
187
188         return 0;
189 }
190
191 static int
192 print_literal(safe_string *ss, char *s)
193 {
194         return(append_to_safe_string(ss,s));
195 }
196
197 static int
198 print_whsp(safe_string *ss)
199 {
200         if ( ss->at_whsp )
201                 return(append_to_safe_string(ss,""));
202         else
203                 return(append_to_safe_string(ss," "));
204 }
205
206 static int
207 print_numericoid(safe_string *ss, char *s)
208 {
209         if ( s )
210                 return(append_to_safe_string(ss,s));
211         else
212                 return(append_to_safe_string(ss,""));
213 }
214
215 /* This one is identical to print_qdescr */
216 static int
217 print_qdstring(safe_string *ss, char *s)
218 {
219         print_whsp(ss);
220         print_literal(ss,"'");
221         append_to_safe_string(ss,s);
222         print_literal(ss,"'");
223         return(print_whsp(ss));
224 }
225
226 static int
227 print_qdescr(safe_string *ss, char *s)
228 {
229         print_whsp(ss);
230         print_literal(ss,"'");
231         append_to_safe_string(ss,s);
232         print_literal(ss,"'");
233         return(print_whsp(ss));
234 }
235
236 static int
237 print_qdescrlist(safe_string *ss, char **sa)
238 {
239         char **sp;
240         int ret = 0;
241         
242         for (sp=sa; *sp; sp++) {
243                 ret = print_qdescr(ss,*sp);
244         }
245         /* If the list was empty, we return zero that is potentially
246          * incorrect, but since we will be still appending things, the
247          * overflow will be detected later.  Maybe FIX.
248          */
249         return(ret);
250 }
251
252 static int
253 print_qdescrs(safe_string *ss, char **sa)
254 {
255         /* The only way to represent an empty list is as a qdescrlist
256          * so, if the list is empty we treat it as a long list.
257          * Really, this is what the syntax mandates.  We should not
258          * be here if the list was empty, but if it happens, a label
259          * has already been output and we cannot undo it.
260          */
261         if ( !sa[0] || ( sa[0] && sa[1] ) ) {
262                 print_whsp(ss);
263                 print_literal(ss,"("/*)*/);
264                 print_qdescrlist(ss,sa);
265                 print_literal(ss,/*(*/")");
266                 return(print_whsp(ss));
267         } else {
268           return(print_qdescr(ss,*sa));
269         }
270 }
271
272 static int
273 print_woid(safe_string *ss, char *s)
274 {
275         print_whsp(ss);
276         append_to_safe_string(ss,s);
277         return print_whsp(ss);
278 }
279
280 static int
281 print_oidlist(safe_string *ss, char **sa)
282 {
283         char **sp;
284
285         for (sp=sa; *(sp+1); sp++) {
286                 print_woid(ss,*sp);
287                 print_literal(ss,"$");
288         }
289         return(print_woid(ss,*sp));
290 }
291
292 static int
293 print_oids(safe_string *ss, char **sa)
294 {
295         if ( sa[0] && sa[1] ) {
296                 print_literal(ss,"("/*)*/);
297                 print_oidlist(ss,sa);
298                 print_whsp(ss);
299                 return(print_literal(ss,/*(*/")"));
300         } else {
301                 return(print_woid(ss,*sa));
302         }
303 }
304
305 static int
306 print_noidlen(safe_string *ss, char *s, int l)
307 {
308         char buf[64];
309         int ret;
310
311         ret = print_numericoid(ss,s);
312         if ( l ) {
313                 snprintf(buf, sizeof buf, "{%d}",l);
314                 ret = print_literal(ss,buf);
315         }
316         return(ret);
317 }
318
319 static int
320 print_ruleid(safe_string *ss, int rid)
321 {
322         char buf[64];
323         snprintf(buf, sizeof buf, "%d", rid);
324         return print_literal(ss,buf);
325 }
326
327 static int
328 print_ruleids(safe_string *ss, int n, int *rids)
329 {
330         int i;
331
332         if( n == 1 ) {
333                 print_ruleid(ss,rids[0]);
334                 return print_whsp(ss);
335         } else {
336                 print_literal(ss,"("/*)*/);
337                 for( i=0; i<n; i++ ) {
338                         print_whsp(ss);
339                         print_ruleid(ss,rids[i]);
340                 }
341                 print_whsp(ss);
342                 return print_literal(ss,/*(*/")");
343         }
344 }
345
346
347 static int
348 print_extensions(safe_string *ss, LDAPSchemaExtensionItem **extensions)
349 {
350         LDAPSchemaExtensionItem **ext;
351
352         if ( extensions ) {
353                 print_whsp(ss);
354                 for ( ext = extensions; *ext != NULL; ext++ ) {
355                         print_literal(ss, (*ext)->lsei_name);
356                         print_whsp(ss);
357                         /* Should be print_qdstrings */
358                         print_qdescrs(ss, (*ext)->lsei_values);
359                         print_whsp(ss);
360                 }
361         }
362
363         return 0;
364 }
365
366 char *
367 ldap_syntax2str( LDAPSyntax * syn )
368 {
369         struct berval bv;
370         if (ldap_syntax2bv( syn, &bv ))
371                 return(bv.bv_val);
372         else
373                 return NULL;
374 }
375
376 struct berval *
377 ldap_syntax2bv( LDAPSyntax * syn, struct berval *bv )
378 {
379         safe_string * ss;
380         
381         ss = new_safe_string(256);
382         if ( !ss )
383                 return NULL;
384
385         print_literal(ss,"("/*)*/);
386         print_whsp(ss);
387
388         print_numericoid(ss, syn->syn_oid);
389         print_whsp(ss);
390
391         if ( syn->syn_desc ) {
392                 print_literal(ss,"DESC");
393                 print_qdstring(ss,syn->syn_desc);
394         }
395
396         print_whsp(ss);
397
398         print_extensions(ss, syn->syn_extensions);
399
400         print_literal(ss,/*(*/ ")");
401
402         bv->bv_val = safe_strdup(ss);
403         bv->bv_len = ss->pos;
404         safe_string_free(ss);
405         return(bv);
406 }
407
408 char *
409 ldap_matchingrule2str( LDAPMatchingRule * mr )
410 {
411         struct berval bv;
412         if (ldap_matchingrule2bv( mr, &bv ))
413                 return(bv.bv_val);
414         else
415                 return NULL;
416 }
417
418 struct berval *
419 ldap_matchingrule2bv( LDAPMatchingRule * mr, struct berval *bv )
420 {
421         safe_string * ss;
422         
423         ss = new_safe_string(256);
424         if ( !ss )
425                 return NULL;
426
427         print_literal(ss,"(" /*)*/);
428         print_whsp(ss);
429
430         print_numericoid(ss, mr->mr_oid);
431         print_whsp(ss);
432
433         if ( mr->mr_names ) {
434                 print_literal(ss,"NAME");
435                 print_qdescrs(ss,mr->mr_names);
436         }
437
438         if ( mr->mr_desc ) {
439                 print_literal(ss,"DESC");
440                 print_qdstring(ss,mr->mr_desc);
441         }
442
443         if ( mr->mr_obsolete ) {
444                 print_literal(ss, "OBSOLETE");
445                 print_whsp(ss);
446         }
447
448         if ( mr->mr_syntax_oid ) {
449                 print_literal(ss,"SYNTAX");
450                 print_whsp(ss);
451                 print_literal(ss, mr->mr_syntax_oid);
452                 print_whsp(ss);
453         }
454
455         print_whsp(ss);
456
457         print_extensions(ss, mr->mr_extensions);
458
459         print_literal(ss,/*(*/")");
460
461         bv->bv_val = safe_strdup(ss);
462         bv->bv_len = ss->pos;
463         safe_string_free(ss);
464         return(bv);
465 }
466
467 char *
468 ldap_matchingruleuse2str( LDAPMatchingRuleUse * mru )
469 {
470         struct berval bv;
471         if (ldap_matchingruleuse2bv( mru, &bv ))
472                 return(bv.bv_val);
473         else
474                 return NULL;
475 }
476
477 struct berval *
478 ldap_matchingruleuse2bv( LDAPMatchingRuleUse * mru, struct berval *bv )
479 {
480         safe_string * ss;
481         
482         ss = new_safe_string(256);
483         if ( !ss )
484                 return NULL;
485
486         print_literal(ss,"(" /*)*/);
487         print_whsp(ss);
488
489         print_numericoid(ss, mru->mru_oid);
490         print_whsp(ss);
491
492         if ( mru->mru_names ) {
493                 print_literal(ss,"NAME");
494                 print_qdescrs(ss,mru->mru_names);
495         }
496
497         if ( mru->mru_desc ) {
498                 print_literal(ss,"DESC");
499                 print_qdstring(ss,mru->mru_desc);
500         }
501
502         if ( mru->mru_obsolete ) {
503                 print_literal(ss, "OBSOLETE");
504                 print_whsp(ss);
505         }
506
507         if ( mru->mru_applies_oids ) {
508                 print_literal(ss,"APPLIES");
509                 print_whsp(ss);
510                 print_oids(ss, mru->mru_applies_oids);
511                 print_whsp(ss);
512         }
513
514         print_whsp(ss);
515
516         print_extensions(ss, mru->mru_extensions);
517
518         print_literal(ss,/*(*/")");
519
520         bv->bv_val = safe_strdup(ss);
521         bv->bv_len = ss->pos;
522         safe_string_free(ss);
523         return(bv);
524 }
525
526 char *
527 ldap_objectclass2str( LDAPObjectClass * oc )
528 {
529         struct berval bv;
530         if (ldap_objectclass2bv( oc, &bv ))
531                 return(bv.bv_val);
532         else
533                 return NULL;
534 }
535
536 struct berval *
537 ldap_objectclass2bv( LDAPObjectClass * oc, struct berval *bv )
538 {
539         safe_string * ss;
540         
541         ss = new_safe_string(256);
542         if ( !ss )
543                 return NULL;
544
545         print_literal(ss,"("/*)*/);
546         print_whsp(ss);
547
548         print_numericoid(ss, oc->oc_oid);
549         print_whsp(ss);
550
551         if ( oc->oc_names ) {
552                 print_literal(ss,"NAME");
553                 print_qdescrs(ss,oc->oc_names);
554         }
555
556         if ( oc->oc_desc ) {
557                 print_literal(ss,"DESC");
558                 print_qdstring(ss,oc->oc_desc);
559         }
560
561         if ( oc->oc_obsolete ) {
562                 print_literal(ss, "OBSOLETE");
563                 print_whsp(ss);
564         }
565
566         if ( oc->oc_sup_oids ) {
567                 print_literal(ss,"SUP");
568                 print_whsp(ss);
569                 print_oids(ss,oc->oc_sup_oids);
570                 print_whsp(ss);
571         }
572
573         switch (oc->oc_kind) {
574         case LDAP_SCHEMA_ABSTRACT:
575                 print_literal(ss,"ABSTRACT");
576                 break;
577         case LDAP_SCHEMA_STRUCTURAL:
578                 print_literal(ss,"STRUCTURAL");
579                 break;
580         case LDAP_SCHEMA_AUXILIARY:
581                 print_literal(ss,"AUXILIARY");
582                 break;
583         default:
584                 print_literal(ss,"KIND-UNKNOWN");
585                 break;
586         }
587         print_whsp(ss);
588         
589         if ( oc->oc_at_oids_must ) {
590                 print_literal(ss,"MUST");
591                 print_whsp(ss);
592                 print_oids(ss,oc->oc_at_oids_must);
593                 print_whsp(ss);
594         }
595
596         if ( oc->oc_at_oids_may ) {
597                 print_literal(ss,"MAY");
598                 print_whsp(ss);
599                 print_oids(ss,oc->oc_at_oids_may);
600                 print_whsp(ss);
601         }
602
603         print_whsp(ss);
604
605         print_extensions(ss, oc->oc_extensions);
606
607         print_literal(ss, /*(*/")");
608
609         bv->bv_val = safe_strdup(ss);
610         bv->bv_len = ss->pos;
611         safe_string_free(ss);
612         return(bv);
613 }
614
615 char *
616 ldap_contentrule2str( LDAPContentRule * cr )
617 {
618         struct berval bv;
619         if (ldap_contentrule2bv( cr, &bv ))
620                 return(bv.bv_val);
621         else
622                 return NULL;
623 }
624
625 struct berval *
626 ldap_contentrule2bv( LDAPContentRule * cr, struct berval *bv )
627 {
628         safe_string * ss;
629         
630         ss = new_safe_string(256);
631         if ( !ss )
632                 return NULL;
633
634         print_literal(ss,"("/*)*/);
635         print_whsp(ss);
636
637         print_numericoid(ss, cr->cr_oid);
638         print_whsp(ss);
639
640         if ( cr->cr_names ) {
641                 print_literal(ss,"NAME");
642                 print_qdescrs(ss,cr->cr_names);
643         }
644
645         if ( cr->cr_desc ) {
646                 print_literal(ss,"DESC");
647                 print_qdstring(ss,cr->cr_desc);
648         }
649
650         if ( cr->cr_obsolete ) {
651                 print_literal(ss, "OBSOLETE");
652                 print_whsp(ss);
653         }
654
655         if ( cr->cr_oc_oids_aux ) {
656                 print_literal(ss,"AUX");
657                 print_whsp(ss);
658                 print_oids(ss,cr->cr_oc_oids_aux);
659                 print_whsp(ss);
660         }
661
662         if ( cr->cr_at_oids_must ) {
663                 print_literal(ss,"MUST");
664                 print_whsp(ss);
665                 print_oids(ss,cr->cr_at_oids_must);
666                 print_whsp(ss);
667         }
668
669         if ( cr->cr_at_oids_may ) {
670                 print_literal(ss,"MAY");
671                 print_whsp(ss);
672                 print_oids(ss,cr->cr_at_oids_may);
673                 print_whsp(ss);
674         }
675
676         if ( cr->cr_at_oids_not ) {
677                 print_literal(ss,"NOT");
678                 print_whsp(ss);
679                 print_oids(ss,cr->cr_at_oids_not);
680                 print_whsp(ss);
681         }
682
683         print_whsp(ss);
684         print_extensions(ss, cr->cr_extensions);
685
686         print_literal(ss, /*(*/")");
687
688         bv->bv_val = safe_strdup(ss);
689         bv->bv_len = ss->pos;
690         safe_string_free(ss);
691         return(bv);
692 }
693
694 char *
695 ldap_structurerule2str( LDAPStructureRule * sr )
696 {
697         struct berval bv;
698         if (ldap_structurerule2bv( sr, &bv ))
699                 return(bv.bv_val);
700         else
701                 return NULL;
702 }
703
704 struct berval *
705 ldap_structurerule2bv( LDAPStructureRule * sr, struct berval *bv )
706 {
707         safe_string * ss;
708         
709         ss = new_safe_string(256);
710         if ( !ss )
711                 return NULL;
712
713         print_literal(ss,"("/*)*/);
714         print_whsp(ss);
715
716         print_ruleid(ss, sr->sr_ruleid);
717         print_whsp(ss);
718
719         if ( sr->sr_names ) {
720                 print_literal(ss,"NAME");
721                 print_qdescrs(ss,sr->sr_names);
722         }
723
724         if ( sr->sr_desc ) {
725                 print_literal(ss,"DESC");
726                 print_qdstring(ss,sr->sr_desc);
727         }
728
729         if ( sr->sr_obsolete ) {
730                 print_literal(ss, "OBSOLETE");
731                 print_whsp(ss);
732         }
733
734         print_literal(ss,"FORM");
735         print_whsp(ss);
736         print_woid(ss,sr->sr_nameform);
737         print_whsp(ss);
738
739         if ( sr->sr_nsup_ruleids ) {
740                 print_literal(ss,"SUP");
741                 print_whsp(ss);
742                 print_ruleids(ss,sr->sr_nsup_ruleids,sr->sr_sup_ruleids);
743                 print_whsp(ss);
744         }
745
746         print_whsp(ss);
747         print_extensions(ss, sr->sr_extensions);
748
749         print_literal(ss, /*(*/")");
750
751         bv->bv_val = safe_strdup(ss);
752         bv->bv_len = ss->pos;
753         safe_string_free(ss);
754         return(bv);
755 }
756
757
758 char *
759 ldap_nameform2str( LDAPNameForm * nf )
760 {
761         struct berval bv;
762         if (ldap_nameform2bv( nf, &bv ))
763                 return(bv.bv_val);
764         else
765                 return NULL;
766 }
767
768 struct berval *
769 ldap_nameform2bv( LDAPNameForm * nf, struct berval *bv )
770 {
771         safe_string * ss;
772         
773         ss = new_safe_string(256);
774         if ( !ss )
775                 return NULL;
776
777         print_literal(ss,"("/*)*/);
778         print_whsp(ss);
779
780         print_numericoid(ss, nf->nf_oid);
781         print_whsp(ss);
782
783         if ( nf->nf_names ) {
784                 print_literal(ss,"NAME");
785                 print_qdescrs(ss,nf->nf_names);
786         }
787
788         if ( nf->nf_desc ) {
789                 print_literal(ss,"DESC");
790                 print_qdstring(ss,nf->nf_desc);
791         }
792
793         if ( nf->nf_obsolete ) {
794                 print_literal(ss, "OBSOLETE");
795                 print_whsp(ss);
796         }
797
798         print_literal(ss,"OC");
799         print_whsp(ss);
800         print_woid(ss,nf->nf_objectclass);
801         print_whsp(ss);
802
803         print_literal(ss,"MUST");
804         print_whsp(ss);
805         print_oids(ss,nf->nf_at_oids_must);
806         print_whsp(ss);
807
808
809         if ( nf->nf_at_oids_may ) {
810                 print_literal(ss,"MAY");
811                 print_whsp(ss);
812                 print_oids(ss,nf->nf_at_oids_may);
813                 print_whsp(ss);
814         }
815
816         print_whsp(ss);
817         print_extensions(ss, nf->nf_extensions);
818
819         print_literal(ss, /*(*/")");
820
821         bv->bv_val = safe_strdup(ss);
822         bv->bv_len = ss->pos;
823         safe_string_free(ss);
824         return(bv);
825 }
826
827 char *
828 ldap_attributetype2str( LDAPAttributeType * at )
829 {
830         struct berval bv;
831         if (ldap_attributetype2bv( at, &bv ))
832                 return(bv.bv_val);
833         else
834                 return NULL;
835 }
836
837 struct berval *
838 ldap_attributetype2bv(  LDAPAttributeType * at, struct berval *bv )
839 {
840         safe_string * ss;
841         
842         ss = new_safe_string(256);
843         if ( !ss )
844                 return NULL;
845
846         print_literal(ss,"("/*)*/);
847         print_whsp(ss);
848
849         print_numericoid(ss, at->at_oid);
850         print_whsp(ss);
851
852         if ( at->at_names ) {
853                 print_literal(ss,"NAME");
854                 print_qdescrs(ss,at->at_names);
855         }
856
857         if ( at->at_desc ) {
858                 print_literal(ss,"DESC");
859                 print_qdstring(ss,at->at_desc);
860         }
861
862         if ( at->at_obsolete ) {
863                 print_literal(ss, "OBSOLETE");
864                 print_whsp(ss);
865         }
866
867         if ( at->at_sup_oid ) {
868                 print_literal(ss,"SUP");
869                 print_woid(ss,at->at_sup_oid);
870         }
871
872         if ( at->at_equality_oid ) {
873                 print_literal(ss,"EQUALITY");
874                 print_woid(ss,at->at_equality_oid);
875         }
876
877         if ( at->at_ordering_oid ) {
878                 print_literal(ss,"ORDERING");
879                 print_woid(ss,at->at_ordering_oid);
880         }
881
882         if ( at->at_substr_oid ) {
883                 print_literal(ss,"SUBSTR");
884                 print_woid(ss,at->at_substr_oid);
885         }
886
887         if ( at->at_syntax_oid ) {
888                 print_literal(ss,"SYNTAX");
889                 print_whsp(ss);
890                 print_noidlen(ss,at->at_syntax_oid,at->at_syntax_len);
891                 print_whsp(ss);
892         }
893
894         if ( at->at_single_value == LDAP_SCHEMA_YES ) {
895                 print_literal(ss,"SINGLE-VALUE");
896                 print_whsp(ss);
897         }
898
899         if ( at->at_collective == LDAP_SCHEMA_YES ) {
900                 print_literal(ss,"COLLECTIVE");
901                 print_whsp(ss);
902         }
903
904         if ( at->at_no_user_mod == LDAP_SCHEMA_YES ) {
905                 print_literal(ss,"NO-USER-MODIFICATION");
906                 print_whsp(ss);
907         }
908
909         if ( at->at_usage != LDAP_SCHEMA_USER_APPLICATIONS ) {
910                 print_literal(ss,"USAGE");
911                 print_whsp(ss);
912                 switch (at->at_usage) {
913                 case LDAP_SCHEMA_DIRECTORY_OPERATION:
914                         print_literal(ss,"directoryOperation");
915                         break;
916                 case LDAP_SCHEMA_DISTRIBUTED_OPERATION:
917                         print_literal(ss,"distributedOperation");
918                         break;
919                 case LDAP_SCHEMA_DSA_OPERATION:
920                         print_literal(ss,"dSAOperation");
921                         break;
922                 default:
923                         print_literal(ss,"UNKNOWN");
924                         break;
925                 }
926         }
927         
928         print_whsp(ss);
929
930         print_extensions(ss, at->at_extensions);
931
932         print_literal(ss,/*(*/")");
933
934         bv->bv_val = safe_strdup(ss);
935         bv->bv_len = ss->pos;
936         safe_string_free(ss);
937         return(bv);
938 }
939
940 /*
941  * Now come the parsers.  There is one parser for each entity type:
942  * objectclasses, attributetypes, etc.
943  *
944  * Each of them is written as a recursive-descent parser, except that
945  * none of them is really recursive.  But the idea is kept: there
946  * is one routine per non-terminal that eithers gobbles lexical tokens
947  * or calls lower-level routines, etc.
948  *
949  * The scanner is implemented in the routine get_token.  Actually,
950  * get_token is more than a scanner and will return tokens that are
951  * in fact non-terminals in the grammar.  So you can see the whole
952  * approach as the combination of a low-level bottom-up recognizer
953  * combined with a scanner and a number of top-down parsers.  Or just
954  * consider that the real grammars recognized by the parsers are not
955  * those of the standards.  As a matter of fact, our parsers are more
956  * liberal than the spec when there is no ambiguity.
957  *
958  * The difference is pretty academic (modulo bugs or incorrect
959  * interpretation of the specs).
960  */
961
962 typedef enum tk_t {
963         TK_NOENDQUOTE   = -2,
964         TK_OUTOFMEM     = -1,
965         TK_EOS          = 0,
966         TK_UNEXPCHAR    = 1,
967         TK_BAREWORD     = 2,
968         TK_QDSTRING     = 3,
969         TK_LEFTPAREN    = 4,
970         TK_RIGHTPAREN   = 5,
971         TK_DOLLAR       = 6,
972         TK_QDESCR       = TK_QDSTRING
973 } tk_t;
974
975 static tk_t
976 get_token( const char ** sp, char ** token_val )
977 {
978         tk_t kind;
979         const char * p;
980         const char * q;
981         char * res;
982
983         *token_val = NULL;
984         switch (**sp) {
985         case '\0':
986                 kind = TK_EOS;
987                 (*sp)++;
988                 break;
989         case '(':
990                 kind = TK_LEFTPAREN;
991                 (*sp)++;
992                 break;
993         case ')':
994                 kind = TK_RIGHTPAREN;
995                 (*sp)++;
996                 break;
997         case '$':
998                 kind = TK_DOLLAR;
999                 (*sp)++;
1000                 break;
1001         case '\'':
1002                 kind = TK_QDSTRING;
1003                 (*sp)++;
1004                 p = *sp;
1005                 while ( **sp != '\'' && **sp != '\0' )
1006                         (*sp)++;
1007                 if ( **sp == '\'' ) {
1008                         q = *sp;
1009                         res = LDAP_MALLOC(q-p+1);
1010                         if ( !res ) {
1011                                 kind = TK_OUTOFMEM;
1012                         } else {
1013                                 strncpy(res,p,q-p);
1014                                 res[q-p] = '\0';
1015                                 *token_val = res;
1016                         }
1017                         (*sp)++;
1018                 } else {
1019                         kind = TK_NOENDQUOTE;
1020                 }
1021                 break;
1022         default:
1023                 kind = TK_BAREWORD;
1024                 p = *sp;
1025                 while ( !LDAP_SPACE(**sp) &&
1026                         **sp != '(' &&
1027                         **sp != ')' &&
1028                         **sp != '$' &&
1029                         **sp != '\'' &&
1030                         **sp != '\0' )
1031                         (*sp)++;
1032                 q = *sp;
1033                 res = LDAP_MALLOC(q-p+1);
1034                 if ( !res ) {
1035                         kind = TK_OUTOFMEM;
1036                 } else {
1037                         strncpy(res,p,q-p);
1038                         res[q-p] = '\0';
1039                         *token_val = res;
1040                 }
1041                 break;
1042 /*              kind = TK_UNEXPCHAR; */
1043 /*              break; */
1044         }
1045         
1046         return kind;
1047 }
1048
1049 /* Gobble optional whitespace */
1050 static void
1051 parse_whsp(const char **sp)
1052 {
1053         while (LDAP_SPACE(**sp))
1054                 (*sp)++;
1055 }
1056
1057 /* TBC:!!
1058  * General note for all parsers: to guarantee the algorithm halts they
1059  * must always advance the pointer even when an error is found.  For
1060  * this one is not that important since an error here is fatal at the
1061  * upper layers, but it is a simple strategy that will not get in
1062  * endless loops.
1063  */
1064
1065 /* Parse a sequence of dot-separated decimal strings */
1066 char *
1067 ldap_int_parse_numericoid(const char **sp, int *code, const int flags)
1068 {
1069         char * res = NULL;
1070         const char * start = *sp;
1071         int len;
1072         int quoted = 0;
1073
1074         /* Netscape puts the SYNTAX value in quotes (incorrectly) */
1075         if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && **sp == '\'' ) {
1076                 quoted = 1;
1077                 (*sp)++;
1078                 start++;
1079         }
1080         /* Each iteration of this loop gets one decimal string */
1081         while (**sp) {
1082                 if ( !LDAP_DIGIT(**sp) ) {
1083                         /*
1084                          * Initial char is not a digit or char after dot is
1085                          * not a digit
1086                          */
1087                         *code = LDAP_SCHERR_NODIGIT;
1088                         return NULL;
1089                 }
1090                 (*sp)++;
1091                 while ( LDAP_DIGIT(**sp) )
1092                         (*sp)++;
1093                 if ( **sp != '.' )
1094                         break;
1095                 /* Otherwise, gobble the dot and loop again */
1096                 (*sp)++;
1097         }
1098         /* Now *sp points at the char past the numericoid. Perfect. */
1099         len = *sp - start;
1100         if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && quoted ) {
1101                 if ( **sp == '\'' ) {
1102                         (*sp)++;
1103                 } else {
1104                         *code = LDAP_SCHERR_UNEXPTOKEN;
1105                         return NULL;
1106                 }
1107         }
1108         if (flags & LDAP_SCHEMA_SKIP) {
1109                 res = (char *)start;
1110         } else {
1111                 res = LDAP_MALLOC(len+1);
1112                 if (!res) {
1113                         *code = LDAP_SCHERR_OUTOFMEM;
1114                         return(NULL);
1115                 }
1116                 strncpy(res,start,len);
1117                 res[len] = '\0';
1118         }
1119         return(res);
1120 }
1121
1122 /* Parse a sequence of dot-separated decimal strings */
1123 int
1124 ldap_int_parse_ruleid(const char **sp, int *code, const int flags, int *ruleid)
1125 {
1126         *ruleid=0;
1127
1128         if ( !LDAP_DIGIT(**sp) ) {
1129                 *code = LDAP_SCHERR_NODIGIT;
1130                 return -1;
1131         }
1132         *ruleid = (**sp) - '0';
1133         (*sp)++;
1134
1135         while ( LDAP_DIGIT(**sp) ) {
1136                 *ruleid *= 10;
1137                 *ruleid += (**sp) - '0';
1138                 (*sp)++;
1139         }
1140
1141         return 0;
1142 }
1143
1144 /* Parse a qdescr or a list of them enclosed in () */
1145 static char **
1146 parse_qdescrs(const char **sp, int *code)
1147 {
1148         char ** res;
1149         char ** res1;
1150         tk_t kind;
1151         char * sval;
1152         int size;
1153         int pos;
1154
1155         parse_whsp(sp);
1156         kind = get_token(sp,&sval);
1157         if ( kind == TK_LEFTPAREN ) {
1158                 /* Let's presume there will be at least 2 entries */
1159                 size = 3;
1160                 res = LDAP_CALLOC(3,sizeof(char *));
1161                 if ( !res ) {
1162                         *code = LDAP_SCHERR_OUTOFMEM;
1163                         return NULL;
1164                 }
1165                 pos = 0;
1166                 while (1) {
1167                         parse_whsp(sp);
1168                         kind = get_token(sp,&sval);
1169                         if ( kind == TK_RIGHTPAREN )
1170                                 break;
1171                         if ( kind == TK_QDESCR ) {
1172                                 if ( pos == size-2 ) {
1173                                         size++;
1174                                         res1 = LDAP_REALLOC(res,size*sizeof(char *));
1175                                         if ( !res1 ) {
1176                                                 LDAP_VFREE(res);
1177                                                 LDAP_FREE(sval);
1178                                                 *code = LDAP_SCHERR_OUTOFMEM;
1179                                                 return(NULL);
1180                                         }
1181                                         res = res1;
1182                                 }
1183                                 res[pos++] = sval;
1184                                 res[pos] = NULL;
1185                                 parse_whsp(sp);
1186                         } else {
1187                                 LDAP_VFREE(res);
1188                                 LDAP_FREE(sval);
1189                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1190                                 return(NULL);
1191                         }
1192                 }
1193                 parse_whsp(sp);
1194                 return(res);
1195         } else if ( kind == TK_QDESCR ) {
1196                 res = LDAP_CALLOC(2,sizeof(char *));
1197                 if ( !res ) {
1198                         *code = LDAP_SCHERR_OUTOFMEM;
1199                         return NULL;
1200                 }
1201                 res[0] = sval;
1202                 res[1] = NULL;
1203                 parse_whsp(sp);
1204                 return res;
1205         } else {
1206                 LDAP_FREE(sval);
1207                 *code = LDAP_SCHERR_BADNAME;
1208                 return NULL;
1209         }
1210 }
1211
1212 /* Parse a woid */
1213 static char *
1214 parse_woid(const char **sp, int *code)
1215 {
1216         char * sval;
1217         tk_t kind;
1218
1219         parse_whsp(sp);
1220         kind = get_token(sp, &sval);
1221         if ( kind != TK_BAREWORD ) {
1222                 LDAP_FREE(sval);
1223                 *code = LDAP_SCHERR_UNEXPTOKEN;
1224                 return NULL;
1225         }
1226         parse_whsp(sp);
1227         return sval;
1228 }
1229
1230 /* Parse a noidlen */
1231 static char *
1232 parse_noidlen(const char **sp, int *code, int *len, int flags)
1233 {
1234         char * sval;
1235         const char *savepos;
1236         int quoted = 0;
1237         int allow_quoted = ( flags & LDAP_SCHEMA_ALLOW_QUOTED );
1238         int allow_oidmacro = ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO );
1239
1240         *len = 0;
1241         /* Netscape puts the SYNTAX value in quotes (incorrectly) */
1242         if ( allow_quoted && **sp == '\'' ) {
1243                 quoted = 1;
1244                 (*sp)++;
1245         }
1246         savepos = *sp;
1247         sval = ldap_int_parse_numericoid(sp, code, 0);
1248         if ( !sval ) {
1249                 if ( allow_oidmacro
1250                         && *sp == savepos
1251                         && *code == LDAP_SCHERR_NODIGIT )
1252                 {
1253                         if ( get_token(sp, &sval) != TK_BAREWORD ) {
1254                                 if ( sval != NULL ) {
1255                                         LDAP_FREE(sval);
1256                                 }
1257                                 return NULL;
1258                         }
1259                 } else {
1260                         return NULL;
1261                 }
1262         }
1263         if ( **sp == '{' /*}*/ ) {
1264                 (*sp)++;
1265                 *len = atoi(*sp);
1266                 while ( LDAP_DIGIT(**sp) )
1267                         (*sp)++;
1268                 if ( **sp != /*{*/ '}' ) {
1269                         *code = LDAP_SCHERR_UNEXPTOKEN;
1270                         LDAP_FREE(sval);
1271                         return NULL;
1272                 }
1273                 (*sp)++;
1274         }               
1275         if ( allow_quoted && quoted ) {
1276                 if ( **sp == '\'' ) {
1277                         (*sp)++;
1278                 } else {
1279                         *code = LDAP_SCHERR_UNEXPTOKEN;
1280                         LDAP_FREE(sval);
1281                         return NULL;
1282                 }
1283         }
1284         return sval;
1285 }
1286
1287 /*
1288  * Next routine will accept a qdstring in place of an oid if
1289  * allow_quoted is set.  This is necessary to interoperate with
1290  * Netscape Directory server that will improperly quote each oid (at
1291  * least those of the descr kind) in the SUP clause.
1292  */
1293
1294 /* Parse a woid or a $-separated list of them enclosed in () */
1295 static char **
1296 parse_oids(const char **sp, int *code, const int allow_quoted)
1297 {
1298         char ** res;
1299         char ** res1;
1300         tk_t kind;
1301         char * sval;
1302         int size;
1303         int pos;
1304
1305         /*
1306          * Strictly speaking, doing this here accepts whsp before the
1307          * ( at the begining of an oidlist, but this is harmless.  Also,
1308          * we are very liberal in what we accept as an OID.  Maybe
1309          * refine later.
1310          */
1311         parse_whsp(sp);
1312         kind = get_token(sp,&sval);
1313         if ( kind == TK_LEFTPAREN ) {
1314                 /* Let's presume there will be at least 2 entries */
1315                 size = 3;
1316                 res = LDAP_CALLOC(3,sizeof(char *));
1317                 if ( !res ) {
1318                         *code = LDAP_SCHERR_OUTOFMEM;
1319                         return NULL;
1320                 }
1321                 pos = 0;
1322                 parse_whsp(sp);
1323                 kind = get_token(sp,&sval);
1324                 if ( kind == TK_BAREWORD ||
1325                      ( allow_quoted && kind == TK_QDSTRING ) ) {
1326                         res[pos++] = sval;
1327                         res[pos] = NULL;
1328                 } else if ( kind == TK_RIGHTPAREN ) {
1329                         /* FIXME: be liberal in what we accept... */
1330                         parse_whsp(sp);
1331                         LDAP_FREE(res);
1332                         return NULL;
1333                 } else {
1334                         *code = LDAP_SCHERR_UNEXPTOKEN;
1335                         LDAP_FREE(sval);
1336                         LDAP_VFREE(res);
1337                         return NULL;
1338                 }
1339                 parse_whsp(sp);
1340                 while (1) {
1341                         kind = get_token(sp,&sval);
1342                         if ( kind == TK_RIGHTPAREN )
1343                                 break;
1344                         if ( kind == TK_DOLLAR ) {
1345                                 parse_whsp(sp);
1346                                 kind = get_token(sp,&sval);
1347                                 if ( kind == TK_BAREWORD ||
1348                                      ( allow_quoted &&
1349                                        kind == TK_QDSTRING ) ) {
1350                                         if ( pos == size-2 ) {
1351                                                 size++;
1352                                                 res1 = LDAP_REALLOC(res,size*sizeof(char *));
1353                                                 if ( !res1 ) {
1354                                                         LDAP_FREE(sval);
1355                                                         LDAP_VFREE(res);
1356                                                         *code = LDAP_SCHERR_OUTOFMEM;
1357                                                         return(NULL);
1358                                                 }
1359                                                 res = res1;
1360                                         }
1361                                         res[pos++] = sval;
1362                                         res[pos] = NULL;
1363                                 } else {
1364                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1365                                         LDAP_FREE(sval);
1366                                         LDAP_VFREE(res);
1367                                         return NULL;
1368                                 }
1369                                 parse_whsp(sp);
1370                         } else {
1371                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1372                                 LDAP_FREE(sval);
1373                                 LDAP_VFREE(res);
1374                                 return NULL;
1375                         }
1376                 }
1377                 parse_whsp(sp);
1378                 return(res);
1379         } else if ( kind == TK_BAREWORD ||
1380                     ( allow_quoted && kind == TK_QDSTRING ) ) {
1381                 res = LDAP_CALLOC(2,sizeof(char *));
1382                 if ( !res ) {
1383                         LDAP_FREE(sval);
1384                         *code = LDAP_SCHERR_OUTOFMEM;
1385                         return NULL;
1386                 }
1387                 res[0] = sval;
1388                 res[1] = NULL;
1389                 parse_whsp(sp);
1390                 return res;
1391         } else {
1392                 LDAP_FREE(sval);
1393                 *code = LDAP_SCHERR_BADNAME;
1394                 return NULL;
1395         }
1396 }
1397
1398 static int
1399 add_extension(LDAPSchemaExtensionItem ***extensions,
1400               char * name, char ** values)
1401 {
1402         int n;
1403         LDAPSchemaExtensionItem **tmp, *ext;
1404
1405         ext = LDAP_CALLOC(1, sizeof(LDAPSchemaExtensionItem));
1406         if ( !ext )
1407                 return 1;
1408         ext->lsei_name = name;
1409         ext->lsei_values = values;
1410
1411         if ( !*extensions ) {
1412                 *extensions =
1413                   LDAP_CALLOC(2, sizeof(LDAPSchemaExtensionItem *));
1414                 if ( !*extensions )
1415                   return 1;
1416                 n = 0;
1417         } else {
1418                 for ( n=0; (*extensions)[n] != NULL; n++ )
1419                         ;
1420                 tmp = LDAP_REALLOC(*extensions,
1421                                    (n+2)*sizeof(LDAPSchemaExtensionItem *));
1422                 if ( !tmp )
1423                         return 1;
1424                 *extensions = tmp;
1425         }
1426         (*extensions)[n] = ext;
1427         (*extensions)[n+1] = NULL;
1428         return 0;
1429 }
1430
1431 static void
1432 free_extensions(LDAPSchemaExtensionItem **extensions)
1433 {
1434         LDAPSchemaExtensionItem **ext;
1435
1436         if ( extensions ) {
1437                 for ( ext = extensions; *ext != NULL; ext++ ) {
1438                         LDAP_FREE((*ext)->lsei_name);
1439                         LDAP_VFREE((*ext)->lsei_values);
1440                         LDAP_FREE(*ext);
1441                 }
1442                 LDAP_FREE(extensions);
1443         }
1444 }
1445
1446 void
1447 ldap_syntax_free( LDAPSyntax * syn )
1448 {
1449         LDAP_FREE(syn->syn_oid);
1450         if (syn->syn_names) LDAP_VFREE(syn->syn_names);
1451         if (syn->syn_desc) LDAP_FREE(syn->syn_desc);
1452         free_extensions(syn->syn_extensions);
1453         LDAP_FREE(syn);
1454 }
1455
1456 LDAPSyntax *
1457 ldap_str2syntax( LDAP_CONST char * s,
1458         int * code,
1459         LDAP_CONST char ** errp,
1460         LDAP_CONST unsigned flags )
1461 {
1462         tk_t kind;
1463         const char * ss = s;
1464         char * sval;
1465         int seen_name = 0;
1466         int seen_desc = 0;
1467         LDAPSyntax * syn;
1468         char ** ext_vals;
1469
1470         if ( !s ) {
1471                 *code = LDAP_SCHERR_EMPTY;
1472                 *errp = "";
1473                 return NULL;
1474         }
1475
1476         *errp = s;
1477         syn = LDAP_CALLOC(1,sizeof(LDAPSyntax));
1478
1479         if ( !syn ) {
1480                 *code = LDAP_SCHERR_OUTOFMEM;
1481                 return NULL;
1482         }
1483
1484         kind = get_token(&ss,&sval);
1485         if ( kind != TK_LEFTPAREN ) {
1486                 LDAP_FREE(sval);
1487                 *code = LDAP_SCHERR_NOLEFTPAREN;
1488                 ldap_syntax_free(syn);
1489                 return NULL;
1490         }
1491
1492         parse_whsp(&ss);
1493         syn->syn_oid = ldap_int_parse_numericoid(&ss,code,0);
1494         if ( !syn->syn_oid ) {
1495                 *errp = ss;
1496                 ldap_syntax_free(syn);
1497                 return NULL;
1498         }
1499         parse_whsp(&ss);
1500
1501         /*
1502          * Beyond this point we will be liberal and accept the items
1503          * in any order.
1504          */
1505         while (1) {
1506                 kind = get_token(&ss,&sval);
1507                 switch (kind) {
1508                 case TK_EOS:
1509                         *code = LDAP_SCHERR_NORIGHTPAREN;
1510                         *errp = ss;
1511                         ldap_syntax_free(syn);
1512                         return NULL;
1513                 case TK_RIGHTPAREN:
1514                         return syn;
1515                 case TK_BAREWORD:
1516                         if ( !strcasecmp(sval,"NAME") ) {
1517                                 LDAP_FREE(sval);
1518                                 if ( seen_name ) {
1519                                         *code = LDAP_SCHERR_DUPOPT;
1520                                         *errp = ss;
1521                                         ldap_syntax_free(syn);
1522                                         return(NULL);
1523                                 }
1524                                 seen_name = 1;
1525                                 syn->syn_names = parse_qdescrs(&ss,code);
1526                                 if ( !syn->syn_names ) {
1527                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1528                                                 *code = LDAP_SCHERR_BADNAME;
1529                                         *errp = ss;
1530                                         ldap_syntax_free(syn);
1531                                         return NULL;
1532                                 }
1533                         } else if ( !strcasecmp(sval,"DESC") ) {
1534                                 LDAP_FREE(sval);
1535                                 if ( seen_desc ) {
1536                                         *code = LDAP_SCHERR_DUPOPT;
1537                                         *errp = ss;
1538                                         ldap_syntax_free(syn);
1539                                         return(NULL);
1540                                 }
1541                                 seen_desc = 1;
1542                                 parse_whsp(&ss);
1543                                 kind = get_token(&ss,&sval);
1544                                 if ( kind != TK_QDSTRING ) {
1545                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1546                                         *errp = ss;
1547                                         LDAP_FREE(sval);
1548                                         ldap_syntax_free(syn);
1549                                         return NULL;
1550                                 }
1551                                 syn->syn_desc = sval;
1552                                 parse_whsp(&ss);
1553                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1554                                 /* Should be parse_qdstrings */
1555                                 ext_vals = parse_qdescrs(&ss, code);
1556                                 if ( !ext_vals ) {
1557                                         *errp = ss;
1558                                         ldap_syntax_free(syn);
1559                                         return NULL;
1560                                 }
1561                                 if ( add_extension(&syn->syn_extensions,
1562                                                     sval, ext_vals) ) {
1563                                         *code = LDAP_SCHERR_OUTOFMEM;
1564                                         *errp = ss;
1565                                         LDAP_FREE(sval);
1566                                         ldap_syntax_free(syn);
1567                                         return NULL;
1568                                 }
1569                         } else {
1570                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1571                                 *errp = ss;
1572                                 LDAP_FREE(sval);
1573                                 ldap_syntax_free(syn);
1574                                 return NULL;
1575                         }
1576                         break;
1577                 default:
1578                         *code = LDAP_SCHERR_UNEXPTOKEN;
1579                         *errp = ss;
1580                         LDAP_FREE(sval);
1581                         ldap_syntax_free(syn);
1582                         return NULL;
1583                 }
1584         }
1585 }
1586
1587 void
1588 ldap_matchingrule_free( LDAPMatchingRule * mr )
1589 {
1590         LDAP_FREE(mr->mr_oid);
1591         if (mr->mr_names) LDAP_VFREE(mr->mr_names);
1592         if (mr->mr_desc) LDAP_FREE(mr->mr_desc);
1593         if (mr->mr_syntax_oid) LDAP_FREE(mr->mr_syntax_oid);
1594         free_extensions(mr->mr_extensions);
1595         LDAP_FREE(mr);
1596 }
1597
1598 LDAPMatchingRule *
1599 ldap_str2matchingrule( LDAP_CONST char * s,
1600         int * code,
1601         LDAP_CONST char ** errp,
1602         LDAP_CONST unsigned flags )
1603 {
1604         tk_t kind;
1605         const char * ss = s;
1606         char * sval;
1607         int seen_name = 0;
1608         int seen_desc = 0;
1609         int seen_obsolete = 0;
1610         int seen_syntax = 0;
1611         LDAPMatchingRule * mr;
1612         char ** ext_vals;
1613         const char * savepos;
1614
1615         if ( !s ) {
1616                 *code = LDAP_SCHERR_EMPTY;
1617                 *errp = "";
1618                 return NULL;
1619         }
1620
1621         *errp = s;
1622         mr = LDAP_CALLOC(1,sizeof(LDAPMatchingRule));
1623
1624         if ( !mr ) {
1625                 *code = LDAP_SCHERR_OUTOFMEM;
1626                 return NULL;
1627         }
1628
1629         kind = get_token(&ss,&sval);
1630         if ( kind != TK_LEFTPAREN ) {
1631                 *code = LDAP_SCHERR_NOLEFTPAREN;
1632                 LDAP_FREE(sval);
1633                 ldap_matchingrule_free(mr);
1634                 return NULL;
1635         }
1636
1637         parse_whsp(&ss);
1638         savepos = ss;
1639         mr->mr_oid = ldap_int_parse_numericoid(&ss,code,flags);
1640         if ( !mr->mr_oid ) {
1641                 if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
1642                         /* Backtracking */
1643                         ss = savepos;
1644                         kind = get_token(&ss,&sval);
1645                         if ( kind == TK_BAREWORD ) {
1646                                 if ( !strcasecmp(sval, "NAME") ||
1647                                      !strcasecmp(sval, "DESC") ||
1648                                      !strcasecmp(sval, "OBSOLETE") ||
1649                                      !strcasecmp(sval, "SYNTAX") ||
1650                                      !strncasecmp(sval, "X-", 2) ) {
1651                                         /* Missing OID, backtrack */
1652                                         ss = savepos;
1653                                 } else {
1654                                         /* Non-numerical OID, ignore */
1655                                 }
1656                         }
1657                         LDAP_FREE(sval);
1658                 } else {
1659                         *errp = ss;
1660                         ldap_matchingrule_free(mr);
1661                         return NULL;
1662                 }
1663         }
1664         parse_whsp(&ss);
1665
1666         /*
1667          * Beyond this point we will be liberal and accept the items
1668          * in any order.
1669          */
1670         while (1) {
1671                 kind = get_token(&ss,&sval);
1672                 switch (kind) {
1673                 case TK_EOS:
1674                         *code = LDAP_SCHERR_NORIGHTPAREN;
1675                         *errp = ss;
1676                         ldap_matchingrule_free(mr);
1677                         return NULL;
1678                 case TK_RIGHTPAREN:
1679                         if( !seen_syntax ) {
1680                                 *code = LDAP_SCHERR_MISSING;
1681                                 ldap_matchingrule_free(mr);
1682                                 return NULL;
1683                         }
1684                         return mr;
1685                 case TK_BAREWORD:
1686                         if ( !strcasecmp(sval,"NAME") ) {
1687                                 LDAP_FREE(sval);
1688                                 if ( seen_name ) {
1689                                         *code = LDAP_SCHERR_DUPOPT;
1690                                         *errp = ss;
1691                                         ldap_matchingrule_free(mr);
1692                                         return(NULL);
1693                                 }
1694                                 seen_name = 1;
1695                                 mr->mr_names = parse_qdescrs(&ss,code);
1696                                 if ( !mr->mr_names ) {
1697                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1698                                                 *code = LDAP_SCHERR_BADNAME;
1699                                         *errp = ss;
1700                                         ldap_matchingrule_free(mr);
1701                                         return NULL;
1702                                 }
1703                         } else if ( !strcasecmp(sval,"DESC") ) {
1704                                 LDAP_FREE(sval);
1705                                 if ( seen_desc ) {
1706                                         *code = LDAP_SCHERR_DUPOPT;
1707                                         *errp = ss;
1708                                         ldap_matchingrule_free(mr);
1709                                         return(NULL);
1710                                 }
1711                                 seen_desc = 1;
1712                                 parse_whsp(&ss);
1713                                 kind = get_token(&ss,&sval);
1714                                 if ( kind != TK_QDSTRING ) {
1715                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1716                                         *errp = ss;
1717                                         LDAP_FREE(sval);
1718                                         ldap_matchingrule_free(mr);
1719                                         return NULL;
1720                                 }
1721                                 mr->mr_desc = sval;
1722                                 parse_whsp(&ss);
1723                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
1724                                 LDAP_FREE(sval);
1725                                 if ( seen_obsolete ) {
1726                                         *code = LDAP_SCHERR_DUPOPT;
1727                                         *errp = ss;
1728                                         ldap_matchingrule_free(mr);
1729                                         return(NULL);
1730                                 }
1731                                 seen_obsolete = 1;
1732                                 mr->mr_obsolete = LDAP_SCHEMA_YES;
1733                                 parse_whsp(&ss);
1734                         } else if ( !strcasecmp(sval,"SYNTAX") ) {
1735                                 LDAP_FREE(sval);
1736                                 if ( seen_syntax ) {
1737                                         *code = LDAP_SCHERR_DUPOPT;
1738                                         *errp = ss;
1739                                         ldap_matchingrule_free(mr);
1740                                         return(NULL);
1741                                 }
1742                                 seen_syntax = 1;
1743                                 parse_whsp(&ss);
1744                                 mr->mr_syntax_oid =
1745                                         ldap_int_parse_numericoid(&ss,code,flags);
1746                                 if ( !mr->mr_syntax_oid ) {
1747                                         *errp = ss;
1748                                         ldap_matchingrule_free(mr);
1749                                         return NULL;
1750                                 }
1751                                 parse_whsp(&ss);
1752                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1753                                 /* Should be parse_qdstrings */
1754                                 ext_vals = parse_qdescrs(&ss, code);
1755                                 if ( !ext_vals ) {
1756                                         *errp = ss;
1757                                         ldap_matchingrule_free(mr);
1758                                         return NULL;
1759                                 }
1760                                 if ( add_extension(&mr->mr_extensions,
1761                                                     sval, ext_vals) ) {
1762                                         *code = LDAP_SCHERR_OUTOFMEM;
1763                                         *errp = ss;
1764                                         LDAP_FREE(sval);
1765                                         ldap_matchingrule_free(mr);
1766                                         return NULL;
1767                                 }
1768                         } else {
1769                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1770                                 *errp = ss;
1771                                 LDAP_FREE(sval);
1772                                 ldap_matchingrule_free(mr);
1773                                 return NULL;
1774                         }
1775                         break;
1776                 default:
1777                         *code = LDAP_SCHERR_UNEXPTOKEN;
1778                         *errp = ss;
1779                         LDAP_FREE(sval);
1780                         ldap_matchingrule_free(mr);
1781                         return NULL;
1782                 }
1783         }
1784 }
1785
1786 void
1787 ldap_matchingruleuse_free( LDAPMatchingRuleUse * mru )
1788 {
1789         LDAP_FREE(mru->mru_oid);
1790         if (mru->mru_names) LDAP_VFREE(mru->mru_names);
1791         if (mru->mru_desc) LDAP_FREE(mru->mru_desc);
1792         if (mru->mru_applies_oids) LDAP_VFREE(mru->mru_applies_oids);
1793         free_extensions(mru->mru_extensions);
1794         LDAP_FREE(mru);
1795 }
1796
1797 LDAPMatchingRuleUse *
1798 ldap_str2matchingruleuse( LDAP_CONST char * s,
1799         int * code,
1800         LDAP_CONST char ** errp,
1801         LDAP_CONST unsigned flags )
1802 {
1803         tk_t kind;
1804         const char * ss = s;
1805         char * sval;
1806         int seen_name = 0;
1807         int seen_desc = 0;
1808         int seen_obsolete = 0;
1809         int seen_applies = 0;
1810         LDAPMatchingRuleUse * mru;
1811         char ** ext_vals;
1812         const char * savepos;
1813
1814         if ( !s ) {
1815                 *code = LDAP_SCHERR_EMPTY;
1816                 *errp = "";
1817                 return NULL;
1818         }
1819
1820         *errp = s;
1821         mru = LDAP_CALLOC(1,sizeof(LDAPMatchingRuleUse));
1822
1823         if ( !mru ) {
1824                 *code = LDAP_SCHERR_OUTOFMEM;
1825                 return NULL;
1826         }
1827
1828         kind = get_token(&ss,&sval);
1829         if ( kind != TK_LEFTPAREN ) {
1830                 *code = LDAP_SCHERR_NOLEFTPAREN;
1831                 LDAP_FREE(sval);
1832                 ldap_matchingruleuse_free(mru);
1833                 return NULL;
1834         }
1835
1836         parse_whsp(&ss);
1837         savepos = ss;
1838         mru->mru_oid = ldap_int_parse_numericoid(&ss,code,flags);
1839         if ( !mru->mru_oid ) {
1840                 if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
1841                         /* Backtracking */
1842                         ss = savepos;
1843                         kind = get_token(&ss,&sval);
1844                         if ( kind == TK_BAREWORD ) {
1845                                 if ( !strcasecmp(sval, "NAME") ||
1846                                      !strcasecmp(sval, "DESC") ||
1847                                      !strcasecmp(sval, "OBSOLETE") ||
1848                                      !strcasecmp(sval, "APPLIES") ||
1849                                      !strncasecmp(sval, "X-", 2) ) {
1850                                         /* Missing OID, backtrack */
1851                                         ss = savepos;
1852                                 } else {
1853                                         /* Non-numerical OID, ignore */
1854                                 }
1855                         }
1856                         LDAP_FREE(sval);
1857                 } else {
1858                         *errp = ss;
1859                         ldap_matchingruleuse_free(mru);
1860                         return NULL;
1861                 }
1862         }
1863         parse_whsp(&ss);
1864
1865         /*
1866          * Beyond this point we will be liberal and accept the items
1867          * in any order.
1868          */
1869         while (1) {
1870                 kind = get_token(&ss,&sval);
1871                 switch (kind) {
1872                 case TK_EOS:
1873                         *code = LDAP_SCHERR_NORIGHTPAREN;
1874                         *errp = ss;
1875                         ldap_matchingruleuse_free(mru);
1876                         return NULL;
1877                 case TK_RIGHTPAREN:
1878                         if( !seen_applies ) {
1879                                 *code = LDAP_SCHERR_MISSING;
1880                                 ldap_matchingruleuse_free(mru);
1881                                 return NULL;
1882                         }
1883                         return mru;
1884                 case TK_BAREWORD:
1885                         if ( !strcasecmp(sval,"NAME") ) {
1886                                 LDAP_FREE(sval);
1887                                 if ( seen_name ) {
1888                                         *code = LDAP_SCHERR_DUPOPT;
1889                                         *errp = ss;
1890                                         ldap_matchingruleuse_free(mru);
1891                                         return(NULL);
1892                                 }
1893                                 seen_name = 1;
1894                                 mru->mru_names = parse_qdescrs(&ss,code);
1895                                 if ( !mru->mru_names ) {
1896                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1897                                                 *code = LDAP_SCHERR_BADNAME;
1898                                         *errp = ss;
1899                                         ldap_matchingruleuse_free(mru);
1900                                         return NULL;
1901                                 }
1902                         } else if ( !strcasecmp(sval,"DESC") ) {
1903                                 LDAP_FREE(sval);
1904                                 if ( seen_desc ) {
1905                                         *code = LDAP_SCHERR_DUPOPT;
1906                                         *errp = ss;
1907                                         ldap_matchingruleuse_free(mru);
1908                                         return(NULL);
1909                                 }
1910                                 seen_desc = 1;
1911                                 parse_whsp(&ss);
1912                                 kind = get_token(&ss,&sval);
1913                                 if ( kind != TK_QDSTRING ) {
1914                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1915                                         *errp = ss;
1916                                         LDAP_FREE(sval);
1917                                         ldap_matchingruleuse_free(mru);
1918                                         return NULL;
1919                                 }
1920                                 mru->mru_desc = sval;
1921                                 parse_whsp(&ss);
1922                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
1923                                 LDAP_FREE(sval);
1924                                 if ( seen_obsolete ) {
1925                                         *code = LDAP_SCHERR_DUPOPT;
1926                                         *errp = ss;
1927                                         ldap_matchingruleuse_free(mru);
1928                                         return(NULL);
1929                                 }
1930                                 seen_obsolete = 1;
1931                                 mru->mru_obsolete = LDAP_SCHEMA_YES;
1932                                 parse_whsp(&ss);
1933                         } else if ( !strcasecmp(sval,"APPLIES") ) {
1934                                 LDAP_FREE(sval);
1935                                 if ( seen_applies ) {
1936                                         *code = LDAP_SCHERR_DUPOPT;
1937                                         *errp = ss;
1938                                         ldap_matchingruleuse_free(mru);
1939                                         return(NULL);
1940                                 }
1941                                 seen_applies = 1;
1942                                 mru->mru_applies_oids = parse_oids(&ss,
1943                                                              code,
1944                                                              flags);
1945                                 if ( !mru->mru_applies_oids && *code != LDAP_SUCCESS ) {
1946                                         *errp = ss;
1947                                         ldap_matchingruleuse_free(mru);
1948                                         return NULL;
1949                                 }
1950                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1951                                 /* Should be parse_qdstrings */
1952                                 ext_vals = parse_qdescrs(&ss, code);
1953                                 if ( !ext_vals ) {
1954                                         *errp = ss;
1955                                         ldap_matchingruleuse_free(mru);
1956                                         return NULL;
1957                                 }
1958                                 if ( add_extension(&mru->mru_extensions,
1959                                                     sval, ext_vals) ) {
1960                                         *code = LDAP_SCHERR_OUTOFMEM;
1961                                         *errp = ss;
1962                                         LDAP_FREE(sval);
1963                                         ldap_matchingruleuse_free(mru);
1964                                         return NULL;
1965                                 }
1966                         } else {
1967                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1968                                 *errp = ss;
1969                                 LDAP_FREE(sval);
1970                                 ldap_matchingruleuse_free(mru);
1971                                 return NULL;
1972                         }
1973                         break;
1974                 default:
1975                         *code = LDAP_SCHERR_UNEXPTOKEN;
1976                         *errp = ss;
1977                         LDAP_FREE(sval);
1978                         ldap_matchingruleuse_free(mru);
1979                         return NULL;
1980                 }
1981         }
1982 }
1983
1984 void
1985 ldap_attributetype_free(LDAPAttributeType * at)
1986 {
1987         LDAP_FREE(at->at_oid);
1988         if (at->at_names) LDAP_VFREE(at->at_names);
1989         if (at->at_desc) LDAP_FREE(at->at_desc);
1990         if (at->at_sup_oid) LDAP_FREE(at->at_sup_oid);
1991         if (at->at_equality_oid) LDAP_FREE(at->at_equality_oid);
1992         if (at->at_ordering_oid) LDAP_FREE(at->at_ordering_oid);
1993         if (at->at_substr_oid) LDAP_FREE(at->at_substr_oid);
1994         if (at->at_syntax_oid) LDAP_FREE(at->at_syntax_oid);
1995         free_extensions(at->at_extensions);
1996         LDAP_FREE(at);
1997 }
1998
1999 LDAPAttributeType *
2000 ldap_str2attributetype( LDAP_CONST char * s,
2001         int * code,
2002         LDAP_CONST char ** errp,
2003         LDAP_CONST unsigned flags )
2004 {
2005         tk_t kind;
2006         const char * ss = s;
2007         char * sval;
2008         int seen_name = 0;
2009         int seen_desc = 0;
2010         int seen_obsolete = 0;
2011         int seen_sup = 0;
2012         int seen_equality = 0;
2013         int seen_ordering = 0;
2014         int seen_substr = 0;
2015         int seen_syntax = 0;
2016         int seen_usage = 0;
2017         LDAPAttributeType * at;
2018         char ** ext_vals;
2019         const char * savepos;
2020
2021         if ( !s ) {
2022                 *code = LDAP_SCHERR_EMPTY;
2023                 *errp = "";
2024                 return NULL;
2025         }
2026
2027         *errp = s;
2028         at = LDAP_CALLOC(1,sizeof(LDAPAttributeType));
2029
2030         if ( !at ) {
2031                 *code = LDAP_SCHERR_OUTOFMEM;
2032                 return NULL;
2033         }
2034
2035         kind = get_token(&ss,&sval);
2036         if ( kind != TK_LEFTPAREN ) {
2037                 *code = LDAP_SCHERR_NOLEFTPAREN;
2038                 LDAP_FREE(sval);
2039                 ldap_attributetype_free(at);
2040                 return NULL;
2041         }
2042
2043         /*
2044          * Definitions MUST begin with an OID in the numericoid format.
2045          * However, this routine is used by clients to parse the response
2046          * from servers and very well known servers will provide an OID
2047          * in the wrong format or even no OID at all.  We do our best to
2048          * extract info from those servers.
2049          */
2050         parse_whsp(&ss);
2051         savepos = ss;
2052         at->at_oid = ldap_int_parse_numericoid(&ss,code,0);
2053         if ( !at->at_oid ) {
2054                 if ( ( flags & ( LDAP_SCHEMA_ALLOW_NO_OID
2055                                 | LDAP_SCHEMA_ALLOW_OID_MACRO ) )
2056                             && (ss == savepos) ) {
2057                         /* Backtracking */
2058                         ss = savepos;
2059                         kind = get_token(&ss,&sval);
2060                         if ( kind == TK_BAREWORD ) {
2061                                 if ( !strcasecmp(sval, "NAME") ||
2062                                      !strcasecmp(sval, "DESC") ||
2063                                      !strcasecmp(sval, "OBSOLETE") ||
2064                                      !strcasecmp(sval, "SUP") ||
2065                                      !strcasecmp(sval, "EQUALITY") ||
2066                                      !strcasecmp(sval, "ORDERING") ||
2067                                      !strcasecmp(sval, "SUBSTR") ||
2068                                      !strcasecmp(sval, "SYNTAX") ||
2069                                      !strcasecmp(sval, "SINGLE-VALUE") ||
2070                                      !strcasecmp(sval, "COLLECTIVE") ||
2071                                      !strcasecmp(sval, "NO-USER-MODIFICATION") ||
2072                                      !strcasecmp(sval, "USAGE") ||
2073                                      !strncasecmp(sval, "X-", 2) ) {
2074                                         /* Missing OID, backtrack */
2075                                         ss = savepos;
2076                                 } else if ( flags
2077                                         & LDAP_SCHEMA_ALLOW_OID_MACRO) {
2078                                         /* Non-numerical OID ... */
2079                                         int len = ss-savepos;
2080                                         at->at_oid = LDAP_MALLOC(len+1);
2081                                         strncpy(at->at_oid, savepos, len);
2082                                         at->at_oid[len] = 0;
2083                                 }
2084                         }
2085                         LDAP_FREE(sval);
2086                 } else {
2087                         *errp = ss;
2088                         ldap_attributetype_free(at);
2089                         return NULL;
2090                 }
2091         }
2092         parse_whsp(&ss);
2093
2094         /*
2095          * Beyond this point we will be liberal and accept the items
2096          * in any order.
2097          */
2098         while (1) {
2099                 kind = get_token(&ss,&sval);
2100                 switch (kind) {
2101                 case TK_EOS:
2102                         *code = LDAP_SCHERR_NORIGHTPAREN;
2103                         *errp = ss;
2104                         ldap_attributetype_free(at);
2105                         return NULL;
2106                 case TK_RIGHTPAREN:
2107                         return at;
2108                 case TK_BAREWORD:
2109                         if ( !strcasecmp(sval,"NAME") ) {
2110                                 LDAP_FREE(sval);
2111                                 if ( seen_name ) {
2112                                         *code = LDAP_SCHERR_DUPOPT;
2113                                         *errp = ss;
2114                                         ldap_attributetype_free(at);
2115                                         return(NULL);
2116                                 }
2117                                 seen_name = 1;
2118                                 at->at_names = parse_qdescrs(&ss,code);
2119                                 if ( !at->at_names ) {
2120                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
2121                                                 *code = LDAP_SCHERR_BADNAME;
2122                                         *errp = ss;
2123                                         ldap_attributetype_free(at);
2124                                         return NULL;
2125                                 }
2126                         } else if ( !strcasecmp(sval,"DESC") ) {
2127                                 LDAP_FREE(sval);
2128                                 if ( seen_desc ) {
2129                                         *code = LDAP_SCHERR_DUPOPT;
2130                                         *errp = ss;
2131                                         ldap_attributetype_free(at);
2132                                         return(NULL);
2133                                 }
2134                                 seen_desc = 1;
2135                                 parse_whsp(&ss);
2136                                 kind = get_token(&ss,&sval);
2137                                 if ( kind != TK_QDSTRING ) {
2138                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2139                                         *errp = ss;
2140                                         LDAP_FREE(sval);
2141                                         ldap_attributetype_free(at);
2142                                         return NULL;
2143                                 }
2144                                 at->at_desc = sval;
2145                                 parse_whsp(&ss);
2146                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
2147                                 LDAP_FREE(sval);
2148                                 if ( seen_obsolete ) {
2149                                         *code = LDAP_SCHERR_DUPOPT;
2150                                         *errp = ss;
2151                                         ldap_attributetype_free(at);
2152                                         return(NULL);
2153                                 }
2154                                 seen_obsolete = 1;
2155                                 at->at_obsolete = LDAP_SCHEMA_YES;
2156                                 parse_whsp(&ss);
2157                         } else if ( !strcasecmp(sval,"SUP") ) {
2158                                 LDAP_FREE(sval);
2159                                 if ( seen_sup ) {
2160                                         *code = LDAP_SCHERR_DUPOPT;
2161                                         *errp = ss;
2162                                         ldap_attributetype_free(at);
2163                                         return(NULL);
2164                                 }
2165                                 seen_sup = 1;
2166                                 at->at_sup_oid = parse_woid(&ss,code);
2167                                 if ( !at->at_sup_oid ) {
2168                                         *errp = ss;
2169                                         ldap_attributetype_free(at);
2170                                         return NULL;
2171                                 }
2172                         } else if ( !strcasecmp(sval,"EQUALITY") ) {
2173                                 LDAP_FREE(sval);
2174                                 if ( seen_equality ) {
2175                                         *code = LDAP_SCHERR_DUPOPT;
2176                                         *errp = ss;
2177                                         ldap_attributetype_free(at);
2178                                         return(NULL);
2179                                 }
2180                                 seen_equality = 1;
2181                                 at->at_equality_oid = parse_woid(&ss,code);
2182                                 if ( !at->at_equality_oid ) {
2183                                         *errp = ss;
2184                                         ldap_attributetype_free(at);
2185                                         return NULL;
2186                                 }
2187                         } else if ( !strcasecmp(sval,"ORDERING") ) {
2188                                 LDAP_FREE(sval);
2189                                 if ( seen_ordering ) {
2190                                         *code = LDAP_SCHERR_DUPOPT;
2191                                         *errp = ss;
2192                                         ldap_attributetype_free(at);
2193                                         return(NULL);
2194                                 }
2195                                 seen_ordering = 1;
2196                                 at->at_ordering_oid = parse_woid(&ss,code);
2197                                 if ( !at->at_ordering_oid ) {
2198                                         *errp = ss;
2199                                         ldap_attributetype_free(at);
2200                                         return NULL;
2201                                 }
2202                         } else if ( !strcasecmp(sval,"SUBSTR") ) {
2203                                 LDAP_FREE(sval);
2204                                 if ( seen_substr ) {
2205                                         *code = LDAP_SCHERR_DUPOPT;
2206                                         *errp = ss;
2207                                         ldap_attributetype_free(at);
2208                                         return(NULL);
2209                                 }
2210                                 seen_substr = 1;
2211                                 at->at_substr_oid = parse_woid(&ss,code);
2212                                 if ( !at->at_substr_oid ) {
2213                                         *errp = ss;
2214                                         ldap_attributetype_free(at);
2215                                         return NULL;
2216                                 }
2217                         } else if ( !strcasecmp(sval,"SYNTAX") ) {
2218                                 LDAP_FREE(sval);
2219                                 if ( seen_syntax ) {
2220                                         *code = LDAP_SCHERR_DUPOPT;
2221                                         *errp = ss;
2222                                         ldap_attributetype_free(at);
2223                                         return(NULL);
2224                                 }
2225                                 seen_syntax = 1;
2226                                 parse_whsp(&ss);
2227                                 savepos = ss;
2228                                 at->at_syntax_oid =
2229                                         parse_noidlen(&ss,
2230                                                       code,
2231                                                       &at->at_syntax_len,
2232                                                       flags);
2233                                 if ( !at->at_syntax_oid ) {
2234                                     if ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO ) {
2235                                         kind = get_token(&ss,&sval);
2236                                         if (kind == TK_BAREWORD)
2237                                         {
2238                                             char *sp = strchr(sval, '{');
2239                                             at->at_syntax_oid = sval;
2240                                             if (sp)
2241                                             {
2242                                                 *sp++ = 0;
2243                                                 at->at_syntax_len = atoi(sp);
2244                                                 while ( LDAP_DIGIT(*sp) )
2245                                                         sp++;
2246                                                 if ( *sp != '}' ) {
2247                                                     *code = LDAP_SCHERR_UNEXPTOKEN;
2248                                                     *errp = ss;
2249                                                     ldap_attributetype_free(at);
2250                                                     return NULL;
2251                                                 }
2252                                             }
2253                                         }
2254                                     } else {
2255                                         *errp = ss;
2256                                         ldap_attributetype_free(at);
2257                                         return NULL;
2258                                     }
2259                                 }
2260                                 parse_whsp(&ss);
2261                         } else if ( !strcasecmp(sval,"SINGLE-VALUE") ) {
2262                                 LDAP_FREE(sval);
2263                                 if ( at->at_single_value ) {
2264                                         *code = LDAP_SCHERR_DUPOPT;
2265                                         *errp = ss;
2266                                         ldap_attributetype_free(at);
2267                                         return(NULL);
2268                                 }
2269                                 at->at_single_value = LDAP_SCHEMA_YES;
2270                                 parse_whsp(&ss);
2271                         } else if ( !strcasecmp(sval,"COLLECTIVE") ) {
2272                                 LDAP_FREE(sval);
2273                                 if ( at->at_collective ) {
2274                                         *code = LDAP_SCHERR_DUPOPT;
2275                                         *errp = ss;
2276                                         ldap_attributetype_free(at);
2277                                         return(NULL);
2278                                 }
2279                                 at->at_collective = LDAP_SCHEMA_YES;
2280                                 parse_whsp(&ss);
2281                         } else if ( !strcasecmp(sval,"NO-USER-MODIFICATION") ) {
2282                                 LDAP_FREE(sval);
2283                                 if ( at->at_no_user_mod ) {
2284                                         *code = LDAP_SCHERR_DUPOPT;
2285                                         *errp = ss;
2286                                         ldap_attributetype_free(at);
2287                                         return(NULL);
2288                                 }
2289                                 at->at_no_user_mod = LDAP_SCHEMA_YES;
2290                                 parse_whsp(&ss);
2291                         } else if ( !strcasecmp(sval,"USAGE") ) {
2292                                 LDAP_FREE(sval);
2293                                 if ( seen_usage ) {
2294                                         *code = LDAP_SCHERR_DUPOPT;
2295                                         *errp = ss;
2296                                         ldap_attributetype_free(at);
2297                                         return(NULL);
2298                                 }
2299                                 seen_usage = 1;
2300                                 parse_whsp(&ss);
2301                                 kind = get_token(&ss,&sval);
2302                                 if ( kind != TK_BAREWORD ) {
2303                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2304                                         *errp = ss;
2305                                         LDAP_FREE(sval);
2306                                         ldap_attributetype_free(at);
2307                                         return NULL;
2308                                 }
2309                                 if ( !strcasecmp(sval,"userApplications") )
2310                                         at->at_usage =
2311                                             LDAP_SCHEMA_USER_APPLICATIONS;
2312                                 else if ( !strcasecmp(sval,"directoryOperation") )
2313                                         at->at_usage =
2314                                             LDAP_SCHEMA_DIRECTORY_OPERATION;
2315                                 else if ( !strcasecmp(sval,"distributedOperation") )
2316                                         at->at_usage =
2317                                             LDAP_SCHEMA_DISTRIBUTED_OPERATION;
2318                                 else if ( !strcasecmp(sval,"dSAOperation") )
2319                                         at->at_usage =
2320                                             LDAP_SCHEMA_DSA_OPERATION;
2321                                 else {
2322                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2323                                         *errp = ss;
2324                                         LDAP_FREE(sval);
2325                                         ldap_attributetype_free(at);
2326                                         return NULL;
2327                                 }
2328                                 LDAP_FREE(sval);
2329                                 parse_whsp(&ss);
2330                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
2331                                 /* Should be parse_qdstrings */
2332                                 ext_vals = parse_qdescrs(&ss, code);
2333                                 if ( !ext_vals ) {
2334                                         *errp = ss;
2335                                         ldap_attributetype_free(at);
2336                                         return NULL;
2337                                 }
2338                                 if ( add_extension(&at->at_extensions,
2339                                                     sval, ext_vals) ) {
2340                                         *code = LDAP_SCHERR_OUTOFMEM;
2341                                         *errp = ss;
2342                                         LDAP_FREE(sval);
2343                                         ldap_attributetype_free(at);
2344                                         return NULL;
2345                                 }
2346                         } else {
2347                                 *code = LDAP_SCHERR_UNEXPTOKEN;
2348                                 *errp = ss;
2349                                 LDAP_FREE(sval);
2350                                 ldap_attributetype_free(at);
2351                                 return NULL;
2352                         }
2353                         break;
2354                 default:
2355                         *code = LDAP_SCHERR_UNEXPTOKEN;
2356                         *errp = ss;
2357                         LDAP_FREE(sval);
2358                         ldap_attributetype_free(at);
2359                         return NULL;
2360                 }
2361         }
2362 }
2363
2364 void
2365 ldap_objectclass_free(LDAPObjectClass * oc)
2366 {
2367         LDAP_FREE(oc->oc_oid);
2368         if (oc->oc_names) LDAP_VFREE(oc->oc_names);
2369         if (oc->oc_desc) LDAP_FREE(oc->oc_desc);
2370         if (oc->oc_sup_oids) LDAP_VFREE(oc->oc_sup_oids);
2371         if (oc->oc_at_oids_must) LDAP_VFREE(oc->oc_at_oids_must);
2372         if (oc->oc_at_oids_may) LDAP_VFREE(oc->oc_at_oids_may);
2373         free_extensions(oc->oc_extensions);
2374         LDAP_FREE(oc);
2375 }
2376
2377 LDAPObjectClass *
2378 ldap_str2objectclass( LDAP_CONST char * s,
2379         int * code,
2380         LDAP_CONST char ** errp,
2381         LDAP_CONST unsigned flags )
2382 {
2383         tk_t kind;
2384         const char * ss = s;
2385         char * sval;
2386         int seen_name = 0;
2387         int seen_desc = 0;
2388         int seen_obsolete = 0;
2389         int seen_sup = 0;
2390         int seen_kind = 0;
2391         int seen_must = 0;
2392         int seen_may = 0;
2393         LDAPObjectClass * oc;
2394         char ** ext_vals;
2395         const char * savepos;
2396
2397         if ( !s ) {
2398                 *code = LDAP_SCHERR_EMPTY;
2399                 *errp = "";
2400                 return NULL;
2401         }
2402
2403         *errp = s;
2404         oc = LDAP_CALLOC(1,sizeof(LDAPObjectClass));
2405
2406         if ( !oc ) {
2407                 *code = LDAP_SCHERR_OUTOFMEM;
2408                 return NULL;
2409         }
2410         oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
2411
2412         kind = get_token(&ss,&sval);
2413         if ( kind != TK_LEFTPAREN ) {
2414                 *code = LDAP_SCHERR_NOLEFTPAREN;
2415                 LDAP_FREE(sval);
2416                 ldap_objectclass_free(oc);
2417                 return NULL;
2418         }
2419
2420         /*
2421          * Definitions MUST begin with an OID in the numericoid format.
2422          * However, this routine is used by clients to parse the response
2423          * from servers and very well known servers will provide an OID
2424          * in the wrong format or even no OID at all.  We do our best to
2425          * extract info from those servers.
2426          */
2427         parse_whsp(&ss);
2428         savepos = ss;
2429         oc->oc_oid = ldap_int_parse_numericoid(&ss,code,0);
2430         if ( !oc->oc_oid ) {
2431                 if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) {
2432                         /* Backtracking */
2433                         ss = savepos;
2434                         kind = get_token(&ss,&sval);
2435                         if ( kind == TK_BAREWORD ) {
2436                                 if ( !strcasecmp(sval, "NAME") ||
2437                                      !strcasecmp(sval, "DESC") ||
2438                                      !strcasecmp(sval, "OBSOLETE") ||
2439                                      !strcasecmp(sval, "SUP") ||
2440                                      !strcasecmp(sval, "ABSTRACT") ||
2441                                      !strcasecmp(sval, "STRUCTURAL") ||
2442                                      !strcasecmp(sval, "AUXILIARY") ||
2443                                      !strcasecmp(sval, "MUST") ||
2444                                      !strcasecmp(sval, "MAY") ||
2445                                      !strncasecmp(sval, "X-", 2) ) {
2446                                         /* Missing OID, backtrack */
2447                                         ss = savepos;
2448                                 } else if ( flags &
2449                                         LDAP_SCHEMA_ALLOW_OID_MACRO ) {
2450                                         /* Non-numerical OID, ignore */
2451                                         int len = ss-savepos;
2452                                         oc->oc_oid = LDAP_MALLOC(len+1);
2453                                         strncpy(oc->oc_oid, savepos, len);
2454                                         oc->oc_oid[len] = 0;
2455                                 }
2456                         }
2457                         LDAP_FREE(sval);
2458                         *code = 0;
2459                 } else {
2460                         *errp = ss;
2461                         ldap_objectclass_free(oc);
2462                         return NULL;
2463                 }
2464         }
2465         parse_whsp(&ss);
2466
2467         /*
2468          * Beyond this point we will be liberal an accept the items
2469          * in any order.
2470          */
2471         while (1) {
2472                 kind = get_token(&ss,&sval);
2473                 switch (kind) {
2474                 case TK_EOS:
2475                         *code = LDAP_SCHERR_NORIGHTPAREN;
2476                         *errp = ss;
2477                         ldap_objectclass_free(oc);
2478                         return NULL;
2479                 case TK_RIGHTPAREN:
2480                         return oc;
2481                 case TK_BAREWORD:
2482                         if ( !strcasecmp(sval,"NAME") ) {
2483                                 LDAP_FREE(sval);
2484                                 if ( seen_name ) {
2485                                         *code = LDAP_SCHERR_DUPOPT;
2486                                         *errp = ss;
2487                                         ldap_objectclass_free(oc);
2488                                         return(NULL);
2489                                 }
2490                                 seen_name = 1;
2491                                 oc->oc_names = parse_qdescrs(&ss,code);
2492                                 if ( !oc->oc_names ) {
2493                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
2494                                                 *code = LDAP_SCHERR_BADNAME;
2495                                         *errp = ss;
2496                                         ldap_objectclass_free(oc);
2497                                         return NULL;
2498                                 }
2499                         } else if ( !strcasecmp(sval,"DESC") ) {
2500                                 LDAP_FREE(sval);
2501                                 if ( seen_desc ) {
2502                                         *code = LDAP_SCHERR_DUPOPT;
2503                                         *errp = ss;
2504                                         ldap_objectclass_free(oc);
2505                                         return(NULL);
2506                                 }
2507                                 seen_desc = 1;
2508                                 parse_whsp(&ss);
2509                                 kind = get_token(&ss,&sval);
2510                                 if ( kind != TK_QDSTRING ) {
2511                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2512                                         *errp = ss;
2513                                         LDAP_FREE(sval);
2514                                         ldap_objectclass_free(oc);
2515                                         return NULL;
2516                                 }
2517                                 oc->oc_desc = sval;
2518                                 parse_whsp(&ss);
2519                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
2520                                 LDAP_FREE(sval);
2521                                 if ( seen_obsolete ) {
2522                                         *code = LDAP_SCHERR_DUPOPT;
2523                                         *errp = ss;
2524                                         ldap_objectclass_free(oc);
2525                                         return(NULL);
2526                                 }
2527                                 seen_obsolete = 1;
2528                                 oc->oc_obsolete = LDAP_SCHEMA_YES;
2529                                 parse_whsp(&ss);
2530                         } else if ( !strcasecmp(sval,"SUP") ) {
2531                                 LDAP_FREE(sval);
2532                                 if ( seen_sup ) {
2533                                         *code = LDAP_SCHERR_DUPOPT;
2534                                         *errp = ss;
2535                                         ldap_objectclass_free(oc);
2536                                         return(NULL);
2537                                 }
2538                                 seen_sup = 1;
2539                                 oc->oc_sup_oids = parse_oids(&ss,
2540                                                              code,
2541                                                              flags);
2542                                 if ( !oc->oc_sup_oids && *code != LDAP_SUCCESS ) {
2543                                         *errp = ss;
2544                                         ldap_objectclass_free(oc);
2545                                         return NULL;
2546                                 }
2547                                 *code = 0;
2548                         } else if ( !strcasecmp(sval,"ABSTRACT") ) {
2549                                 LDAP_FREE(sval);
2550                                 if ( seen_kind ) {
2551                                         *code = LDAP_SCHERR_DUPOPT;
2552                                         *errp = ss;
2553                                         ldap_objectclass_free(oc);
2554                                         return(NULL);
2555                                 }
2556                                 seen_kind = 1;
2557                                 oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
2558                                 parse_whsp(&ss);
2559                         } else if ( !strcasecmp(sval,"STRUCTURAL") ) {
2560                                 LDAP_FREE(sval);
2561                                 if ( seen_kind ) {
2562                                         *code = LDAP_SCHERR_DUPOPT;
2563                                         *errp = ss;
2564                                         ldap_objectclass_free(oc);
2565                                         return(NULL);
2566                                 }
2567                                 seen_kind = 1;
2568                                 oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
2569                                 parse_whsp(&ss);
2570                         } else if ( !strcasecmp(sval,"AUXILIARY") ) {
2571                                 LDAP_FREE(sval);
2572                                 if ( seen_kind ) {
2573                                         *code = LDAP_SCHERR_DUPOPT;
2574                                         *errp = ss;
2575                                         ldap_objectclass_free(oc);
2576                                         return(NULL);
2577                                 }
2578                                 seen_kind = 1;
2579                                 oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
2580                                 parse_whsp(&ss);
2581                         } else if ( !strcasecmp(sval,"MUST") ) {
2582                                 LDAP_FREE(sval);
2583                                 if ( seen_must ) {
2584                                         *code = LDAP_SCHERR_DUPOPT;
2585                                         *errp = ss;
2586                                         ldap_objectclass_free(oc);
2587                                         return(NULL);
2588                                 }
2589                                 seen_must = 1;
2590                                 oc->oc_at_oids_must = parse_oids(&ss,code,0);
2591                                 if ( !oc->oc_at_oids_must && *code != LDAP_SUCCESS ) {
2592                                         *errp = ss;
2593                                         ldap_objectclass_free(oc);
2594                                         return NULL;
2595                                 }
2596                                 *code = 0;
2597                                 parse_whsp(&ss);
2598                         } else if ( !strcasecmp(sval,"MAY") ) {
2599                                 LDAP_FREE(sval);
2600                                 if ( seen_may ) {
2601                                         *code = LDAP_SCHERR_DUPOPT;
2602                                         *errp = ss;
2603                                         ldap_objectclass_free(oc);
2604                                         return(NULL);
2605                                 }
2606                                 seen_may = 1;
2607                                 oc->oc_at_oids_may = parse_oids(&ss,code,0);
2608                                 if ( !oc->oc_at_oids_may && *code != LDAP_SUCCESS ) {
2609                                         *errp = ss;
2610                                         ldap_objectclass_free(oc);
2611                                         return NULL;
2612                                 }
2613                                 *code = 0;
2614                                 parse_whsp(&ss);
2615                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
2616                                 /* Should be parse_qdstrings */
2617                                 ext_vals = parse_qdescrs(&ss, code);
2618                                 *code = 0;
2619                                 if ( !ext_vals ) {
2620                                         *errp = ss;
2621                                         ldap_objectclass_free(oc);
2622                                         return NULL;
2623                                 }
2624                                 if ( add_extension(&oc->oc_extensions,
2625                                                     sval, ext_vals) ) {
2626                                         *code = LDAP_SCHERR_OUTOFMEM;
2627                                         *errp = ss;
2628                                         LDAP_FREE(sval);
2629                                         ldap_objectclass_free(oc);
2630                                         return NULL;
2631                                 }
2632                         } else {
2633                                 *code = LDAP_SCHERR_UNEXPTOKEN;
2634                                 *errp = ss;
2635                                 LDAP_FREE(sval);
2636                                 ldap_objectclass_free(oc);
2637                                 return NULL;
2638                         }
2639                         break;
2640                 default:
2641                         *code = LDAP_SCHERR_UNEXPTOKEN;
2642                         *errp = ss;
2643                         LDAP_FREE(sval);
2644                         ldap_objectclass_free(oc);
2645                         return NULL;
2646                 }
2647         }
2648 }
2649
2650 void
2651 ldap_contentrule_free(LDAPContentRule * cr)
2652 {
2653         LDAP_FREE(cr->cr_oid);
2654         if (cr->cr_names) LDAP_VFREE(cr->cr_names);
2655         if (cr->cr_desc) LDAP_FREE(cr->cr_desc);
2656         if (cr->cr_oc_oids_aux) LDAP_VFREE(cr->cr_oc_oids_aux);
2657         if (cr->cr_at_oids_must) LDAP_VFREE(cr->cr_at_oids_must);
2658         if (cr->cr_at_oids_may) LDAP_VFREE(cr->cr_at_oids_may);
2659         if (cr->cr_at_oids_not) LDAP_VFREE(cr->cr_at_oids_not);
2660         free_extensions(cr->cr_extensions);
2661         LDAP_FREE(cr);
2662 }
2663
2664 LDAPContentRule *
2665 ldap_str2contentrule( LDAP_CONST char * s,
2666         int * code,
2667         LDAP_CONST char ** errp,
2668         LDAP_CONST unsigned flags )
2669 {
2670         tk_t kind;
2671         const char * ss = s;
2672         char * sval;
2673         int seen_name = 0;
2674         int seen_desc = 0;
2675         int seen_obsolete = 0;
2676         int seen_aux = 0;
2677         int seen_must = 0;
2678         int seen_may = 0;
2679         int seen_not = 0;
2680         LDAPContentRule * cr;
2681         char ** ext_vals;
2682         const char * savepos;
2683
2684         if ( !s ) {
2685                 *code = LDAP_SCHERR_EMPTY;
2686                 *errp = "";
2687                 return NULL;
2688         }
2689
2690         *errp = s;
2691         cr = LDAP_CALLOC(1,sizeof(LDAPContentRule));
2692
2693         if ( !cr ) {
2694                 *code = LDAP_SCHERR_OUTOFMEM;
2695                 return NULL;
2696         }
2697
2698         kind = get_token(&ss,&sval);
2699         if ( kind != TK_LEFTPAREN ) {
2700                 *code = LDAP_SCHERR_NOLEFTPAREN;
2701                 LDAP_FREE(sval);
2702                 ldap_contentrule_free(cr);
2703                 return NULL;
2704         }
2705
2706         /*
2707          * Definitions MUST begin with an OID in the numericoid format.
2708          */
2709         parse_whsp(&ss);
2710         savepos = ss;
2711         cr->cr_oid = ldap_int_parse_numericoid(&ss,code,0);
2712         if ( !cr->cr_oid ) {
2713                 if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) {
2714                         /* Backtracking */
2715                         ss = savepos;
2716                         kind = get_token(&ss,&sval);
2717                         if ( kind == TK_BAREWORD ) {
2718                                 if ( !strcasecmp(sval, "NAME") ||
2719                                      !strcasecmp(sval, "DESC") ||
2720                                      !strcasecmp(sval, "OBSOLETE") ||
2721                                      !strcasecmp(sval, "AUX") ||
2722                                      !strcasecmp(sval, "MUST") ||
2723                                      !strcasecmp(sval, "MAY") ||
2724                                      !strcasecmp(sval, "NOT") ||
2725                                      !strncasecmp(sval, "X-", 2) ) {
2726                                         /* Missing OID, backtrack */
2727                                         ss = savepos;
2728                                 } else if ( flags &
2729                                         LDAP_SCHEMA_ALLOW_OID_MACRO ) {
2730                                         /* Non-numerical OID, ignore */
2731                                         int len = ss-savepos;
2732                                         cr->cr_oid = LDAP_MALLOC(len+1);
2733                                         strncpy(cr->cr_oid, savepos, len);
2734                                         cr->cr_oid[len] = 0;
2735                                 }
2736                         }
2737                         LDAP_FREE(sval);
2738                 } else {
2739                         *errp = ss;
2740                         ldap_contentrule_free(cr);
2741                         return NULL;
2742                 }
2743         }
2744         parse_whsp(&ss);
2745
2746         /*
2747          * Beyond this point we will be liberal an accept the items
2748          * in any order.
2749          */
2750         while (1) {
2751                 kind = get_token(&ss,&sval);
2752                 switch (kind) {
2753                 case TK_EOS:
2754                         *code = LDAP_SCHERR_NORIGHTPAREN;
2755                         *errp = ss;
2756                         ldap_contentrule_free(cr);
2757                         return NULL;
2758                 case TK_RIGHTPAREN:
2759                         return cr;
2760                 case TK_BAREWORD:
2761                         if ( !strcasecmp(sval,"NAME") ) {
2762                                 LDAP_FREE(sval);
2763                                 if ( seen_name ) {
2764                                         *code = LDAP_SCHERR_DUPOPT;
2765                                         *errp = ss;
2766                                         ldap_contentrule_free(cr);
2767                                         return(NULL);
2768                                 }
2769                                 seen_name = 1;
2770                                 cr->cr_names = parse_qdescrs(&ss,code);
2771                                 if ( !cr->cr_names ) {
2772                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
2773                                                 *code = LDAP_SCHERR_BADNAME;
2774                                         *errp = ss;
2775                                         ldap_contentrule_free(cr);
2776                                         return NULL;
2777                                 }
2778                         } else if ( !strcasecmp(sval,"DESC") ) {
2779                                 LDAP_FREE(sval);
2780                                 if ( seen_desc ) {
2781                                         *code = LDAP_SCHERR_DUPOPT;
2782                                         *errp = ss;
2783                                         ldap_contentrule_free(cr);
2784                                         return(NULL);
2785                                 }
2786                                 seen_desc = 1;
2787                                 parse_whsp(&ss);
2788                                 kind = get_token(&ss,&sval);
2789                                 if ( kind != TK_QDSTRING ) {
2790                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2791                                         *errp = ss;
2792                                         LDAP_FREE(sval);
2793                                         ldap_contentrule_free(cr);
2794                                         return NULL;
2795                                 }
2796                                 cr->cr_desc = sval;
2797                                 parse_whsp(&ss);
2798                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
2799                                 LDAP_FREE(sval);
2800                                 if ( seen_obsolete ) {
2801                                         *code = LDAP_SCHERR_DUPOPT;
2802                                         *errp = ss;
2803                                         ldap_contentrule_free(cr);
2804                                         return(NULL);
2805                                 }
2806                                 seen_obsolete = 1;
2807                                 cr->cr_obsolete = LDAP_SCHEMA_YES;
2808                                 parse_whsp(&ss);
2809                         } else if ( !strcasecmp(sval,"AUX") ) {
2810                                 LDAP_FREE(sval);
2811                                 if ( seen_aux ) {
2812                                         *code = LDAP_SCHERR_DUPOPT;
2813                                         *errp = ss;
2814                                         ldap_contentrule_free(cr);
2815                                         return(NULL);
2816                                 }
2817                                 seen_aux = 1;
2818                                 cr->cr_oc_oids_aux = parse_oids(&ss,code,0);
2819                                 if ( !cr->cr_oc_oids_aux ) {
2820                                         *errp = ss;
2821                                         ldap_contentrule_free(cr);
2822                                         return NULL;
2823                                 }
2824                                 parse_whsp(&ss);
2825                         } else if ( !strcasecmp(sval,"MUST") ) {
2826                                 LDAP_FREE(sval);
2827                                 if ( seen_must ) {
2828                                         *code = LDAP_SCHERR_DUPOPT;
2829                                         *errp = ss;
2830                                         ldap_contentrule_free(cr);
2831                                         return(NULL);
2832                                 }
2833                                 seen_must = 1;
2834                                 cr->cr_at_oids_must = parse_oids(&ss,code,0);
2835                                 if ( !cr->cr_at_oids_must && *code != LDAP_SUCCESS ) {
2836                                         *errp = ss;
2837                                         ldap_contentrule_free(cr);
2838                                         return NULL;
2839                                 }
2840                                 parse_whsp(&ss);
2841                         } else if ( !strcasecmp(sval,"MAY") ) {
2842                                 LDAP_FREE(sval);
2843                                 if ( seen_may ) {
2844                                         *code = LDAP_SCHERR_DUPOPT;
2845                                         *errp = ss;
2846                                         ldap_contentrule_free(cr);
2847                                         return(NULL);
2848                                 }
2849                                 seen_may = 1;
2850                                 cr->cr_at_oids_may = parse_oids(&ss,code,0);
2851                                 if ( !cr->cr_at_oids_may && *code != LDAP_SUCCESS ) {
2852                                         *errp = ss;
2853                                         ldap_contentrule_free(cr);
2854                                         return NULL;
2855                                 }
2856                                 parse_whsp(&ss);
2857                         } else if ( !strcasecmp(sval,"NOT") ) {
2858                                 LDAP_FREE(sval);
2859                                 if ( seen_not ) {
2860                                         *code = LDAP_SCHERR_DUPOPT;
2861                                         *errp = ss;
2862                                         ldap_contentrule_free(cr);
2863                                         return(NULL);
2864                                 }
2865                                 seen_not = 1;
2866                                 cr->cr_at_oids_not = parse_oids(&ss,code,0);
2867                                 if ( !cr->cr_at_oids_not && *code != LDAP_SUCCESS ) {
2868                                         *errp = ss;
2869                                         ldap_contentrule_free(cr);
2870                                         return NULL;
2871                                 }
2872                                 parse_whsp(&ss);
2873                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
2874                                 /* Should be parse_qdstrings */
2875                                 ext_vals = parse_qdescrs(&ss, code);
2876                                 if ( !ext_vals ) {
2877                                         *errp = ss;
2878                                         ldap_contentrule_free(cr);
2879                                         return NULL;
2880                                 }
2881                                 if ( add_extension(&cr->cr_extensions,
2882                                                     sval, ext_vals) ) {
2883                                         *code = LDAP_SCHERR_OUTOFMEM;
2884                                         *errp = ss;
2885                                         LDAP_FREE(sval);
2886                                         ldap_contentrule_free(cr);
2887                                         return NULL;
2888                                 }
2889                         } else {
2890                                 *code = LDAP_SCHERR_UNEXPTOKEN;
2891                                 *errp = ss;
2892                                 LDAP_FREE(sval);
2893                                 ldap_contentrule_free(cr);
2894                                 return NULL;
2895                         }
2896                         break;
2897                 default:
2898                         *code = LDAP_SCHERR_UNEXPTOKEN;
2899                         *errp = ss;
2900                         LDAP_FREE(sval);
2901                         ldap_contentrule_free(cr);
2902                         return NULL;
2903                 }
2904         }
2905 }
2906
2907 void
2908 ldap_structurerule_free(LDAPStructureRule * sr)
2909 {
2910         if (sr->sr_names) LDAP_VFREE(sr->sr_names);
2911         if (sr->sr_desc) LDAP_FREE(sr->sr_desc);
2912         if (sr->sr_nameform) LDAP_FREE(sr->sr_nameform);
2913         if (sr->sr_sup_ruleids) LDAP_FREE(sr->sr_sup_ruleids);
2914         free_extensions(sr->sr_extensions);
2915         LDAP_FREE(sr);
2916 }
2917
2918 LDAPStructureRule *
2919 ldap_str2structurerule( LDAP_CONST char * s,
2920         int * code,
2921         LDAP_CONST char ** errp,
2922         LDAP_CONST unsigned flags )
2923 {
2924         tk_t kind;
2925         int ret;
2926         const char * ss = s;
2927         char * sval;
2928         int seen_name = 0;
2929         int seen_desc = 0;
2930         int seen_obsolete = 0;
2931         int seen_nameform = 0;
2932         LDAPStructureRule * sr;
2933         char ** ext_vals;
2934         const char * savepos;
2935
2936         if ( !s ) {
2937                 *code = LDAP_SCHERR_EMPTY;
2938                 *errp = "";
2939                 return NULL;
2940         }
2941
2942         *errp = s;
2943         sr = LDAP_CALLOC(1,sizeof(LDAPStructureRule));
2944
2945         if ( !sr ) {
2946                 *code = LDAP_SCHERR_OUTOFMEM;
2947                 return NULL;
2948         }
2949
2950         kind = get_token(&ss,&sval);
2951         if ( kind != TK_LEFTPAREN ) {
2952                 *code = LDAP_SCHERR_NOLEFTPAREN;
2953                 LDAP_FREE(sval);
2954                 ldap_structurerule_free(sr);
2955                 return NULL;
2956         }
2957
2958         /*
2959          * Definitions MUST begin with a ruleid.
2960          */
2961         parse_whsp(&ss);
2962         savepos = ss;
2963         ret = ldap_int_parse_ruleid(&ss,code,0,&sr->sr_ruleid);
2964         if ( ret ) {
2965                 *errp = ss;
2966                 ldap_structurerule_free(sr);
2967                 return NULL;
2968         }
2969         parse_whsp(&ss);
2970
2971         /*
2972          * Beyond this point we will be liberal an accept the items
2973          * in any order.
2974          */
2975         while (1) {
2976                 kind = get_token(&ss,&sval);
2977                 switch (kind) {
2978                 case TK_EOS:
2979                         *code = LDAP_SCHERR_NORIGHTPAREN;
2980                         *errp = ss;
2981                         ldap_structurerule_free(sr);
2982                         return NULL;
2983                 case TK_RIGHTPAREN:
2984                         if( !seen_nameform ) {
2985                                 *code = LDAP_SCHERR_MISSING;
2986                                 ldap_structurerule_free(sr);
2987                                 return NULL;
2988                         }
2989                         return sr;
2990                 case TK_BAREWORD:
2991                         if ( !strcasecmp(sval,"NAME") ) {
2992                                 LDAP_FREE(sval);
2993                                 if ( seen_name ) {
2994                                         *code = LDAP_SCHERR_DUPOPT;
2995                                         *errp = ss;
2996                                         ldap_structurerule_free(sr);
2997                                         return(NULL);
2998                                 }
2999                                 seen_name = 1;
3000                                 sr->sr_names = parse_qdescrs(&ss,code);
3001                                 if ( !sr->sr_names ) {
3002                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
3003                                                 *code = LDAP_SCHERR_BADNAME;
3004                                         *errp = ss;
3005                                         ldap_structurerule_free(sr);
3006                                         return NULL;
3007                                 }
3008                         } else if ( !strcasecmp(sval,"DESC") ) {
3009                                 LDAP_FREE(sval);
3010                                 if ( seen_desc ) {
3011                                         *code = LDAP_SCHERR_DUPOPT;
3012                                         *errp = ss;
3013                                         ldap_structurerule_free(sr);
3014                                         return(NULL);
3015                                 }
3016                                 seen_desc = 1;
3017                                 parse_whsp(&ss);
3018                                 kind = get_token(&ss,&sval);
3019                                 if ( kind != TK_QDSTRING ) {
3020                                         *code = LDAP_SCHERR_UNEXPTOKEN;
3021                                         *errp = ss;
3022                                         LDAP_FREE(sval);
3023                                         ldap_structurerule_free(sr);
3024                                         return NULL;
3025                                 }
3026                                 sr->sr_desc = sval;
3027                                 parse_whsp(&ss);
3028                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
3029                                 LDAP_FREE(sval);
3030                                 if ( seen_obsolete ) {
3031                                         *code = LDAP_SCHERR_DUPOPT;
3032                                         *errp = ss;
3033                                         ldap_structurerule_free(sr);
3034                                         return(NULL);
3035                                 }
3036                                 seen_obsolete = 1;
3037                                 sr->sr_obsolete = LDAP_SCHEMA_YES;
3038                                 parse_whsp(&ss);
3039                         } else if ( !strcasecmp(sval,"FORM") ) {
3040                                 LDAP_FREE(sval);
3041                                 if ( seen_nameform ) {
3042                                         *code = LDAP_SCHERR_DUPOPT;
3043                                         *errp = ss;
3044                                         ldap_structurerule_free(sr);
3045                                         return(NULL);
3046                                 }
3047                                 seen_nameform = 1;
3048                                 sr->sr_nameform = parse_woid(&ss,code);
3049                                 if ( !sr->sr_nameform ) {
3050                                         *errp = ss;
3051                                         ldap_structurerule_free(sr);
3052                                         return NULL;
3053                                 }
3054                                 parse_whsp(&ss);
3055                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
3056                                 /* Should be parse_qdstrings */
3057                                 ext_vals = parse_qdescrs(&ss, code);
3058                                 if ( !ext_vals ) {
3059                                         *errp = ss;
3060                                         ldap_structurerule_free(sr);
3061                                         return NULL;
3062                                 }
3063                                 if ( add_extension(&sr->sr_extensions,
3064                                                     sval, ext_vals) ) {
3065                                         *code = LDAP_SCHERR_OUTOFMEM;
3066                                         *errp = ss;
3067                                         LDAP_FREE(sval);
3068                                         ldap_structurerule_free(sr);
3069                                         return NULL;
3070                                 }
3071                         } else {
3072                                 *code = LDAP_SCHERR_UNEXPTOKEN;
3073                                 *errp = ss;
3074                                 LDAP_FREE(sval);
3075                                 ldap_structurerule_free(sr);
3076                                 return NULL;
3077                         }
3078                         break;
3079                 default:
3080                         *code = LDAP_SCHERR_UNEXPTOKEN;
3081                         *errp = ss;
3082                         LDAP_FREE(sval);
3083                         ldap_structurerule_free(sr);
3084                         return NULL;
3085                 }
3086         }
3087 }
3088
3089 void
3090 ldap_nameform_free(LDAPNameForm * nf)
3091 {
3092         LDAP_FREE(nf->nf_oid);
3093         if (nf->nf_names) LDAP_VFREE(nf->nf_names);
3094         if (nf->nf_desc) LDAP_FREE(nf->nf_desc);
3095         if (nf->nf_objectclass) LDAP_FREE(nf->nf_objectclass);
3096         if (nf->nf_at_oids_must) LDAP_VFREE(nf->nf_at_oids_must);
3097         if (nf->nf_at_oids_may) LDAP_VFREE(nf->nf_at_oids_may);
3098         free_extensions(nf->nf_extensions);
3099         LDAP_FREE(nf);
3100 }
3101
3102 LDAPNameForm *
3103 ldap_str2nameform( LDAP_CONST char * s,
3104         int * code,
3105         LDAP_CONST char ** errp,
3106         LDAP_CONST unsigned flags )
3107 {
3108         tk_t kind;
3109         const char * ss = s;
3110         char * sval;
3111         int seen_name = 0;
3112         int seen_desc = 0;
3113         int seen_obsolete = 0;
3114         int seen_class = 0;
3115         int seen_must = 0;
3116         int seen_may = 0;
3117         LDAPNameForm * nf;
3118         char ** ext_vals;
3119         const char * savepos;
3120
3121         if ( !s ) {
3122                 *code = LDAP_SCHERR_EMPTY;
3123                 *errp = "";
3124                 return NULL;
3125         }
3126
3127         *errp = s;
3128         nf = LDAP_CALLOC(1,sizeof(LDAPNameForm));
3129
3130         if ( !nf ) {
3131                 *code = LDAP_SCHERR_OUTOFMEM;
3132                 return NULL;
3133         }
3134
3135         kind = get_token(&ss,&sval);
3136         if ( kind != TK_LEFTPAREN ) {
3137                 *code = LDAP_SCHERR_NOLEFTPAREN;
3138                 LDAP_FREE(sval);
3139                 ldap_nameform_free(nf);
3140                 return NULL;
3141         }
3142
3143         /*
3144          * Definitions MUST begin with an OID in the numericoid format.
3145          * However, this routine is used by clients to parse the response
3146          * from servers and very well known servers will provide an OID
3147          * in the wrong format or even no OID at all.  We do our best to
3148          * extract info from those servers.
3149          */
3150         parse_whsp(&ss);
3151         savepos = ss;
3152         nf->nf_oid = ldap_int_parse_numericoid(&ss,code,0);
3153         if ( !nf->nf_oid ) {
3154                 *errp = ss;
3155                 ldap_nameform_free(nf);
3156                 return NULL;
3157         }
3158         parse_whsp(&ss);
3159
3160         /*
3161          * Beyond this point we will be liberal an accept the items
3162          * in any order.
3163          */
3164         while (1) {
3165                 kind = get_token(&ss,&sval);
3166                 switch (kind) {
3167                 case TK_EOS:
3168                         *code = LDAP_SCHERR_NORIGHTPAREN;
3169                         *errp = ss;
3170                         ldap_nameform_free(nf);
3171                         return NULL;
3172                 case TK_RIGHTPAREN:
3173                         if( !seen_class || !seen_must ) {
3174                                 *code = LDAP_SCHERR_MISSING;
3175                                 ldap_nameform_free(nf);
3176                                 return NULL;
3177                         }
3178                         return nf;
3179                 case TK_BAREWORD:
3180                         if ( !strcasecmp(sval,"NAME") ) {
3181                                 LDAP_FREE(sval);
3182                                 if ( seen_name ) {
3183                                         *code = LDAP_SCHERR_DUPOPT;
3184                                         *errp = ss;
3185                                         ldap_nameform_free(nf);
3186                                         return(NULL);
3187                                 }
3188                                 seen_name = 1;
3189                                 nf->nf_names = parse_qdescrs(&ss,code);
3190                                 if ( !nf->nf_names ) {
3191                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
3192                                                 *code = LDAP_SCHERR_BADNAME;
3193                                         *errp = ss;
3194                                         ldap_nameform_free(nf);
3195                                         return NULL;
3196                                 }
3197                         } else if ( !strcasecmp(sval,"DESC") ) {
3198                                 LDAP_FREE(sval);
3199                                 if ( seen_desc ) {
3200                                         *code = LDAP_SCHERR_DUPOPT;
3201                                         *errp = ss;
3202                                         ldap_nameform_free(nf);
3203                                         return(NULL);
3204                                 }
3205                                 seen_desc = 1;
3206                                 parse_whsp(&ss);
3207                                 kind = get_token(&ss,&sval);
3208                                 if ( kind != TK_QDSTRING ) {
3209                                         *code = LDAP_SCHERR_UNEXPTOKEN;
3210                                         *errp = ss;
3211                                         LDAP_FREE(sval);
3212                                         ldap_nameform_free(nf);
3213                                         return NULL;
3214                                 }
3215                                 nf->nf_desc = sval;
3216                                 parse_whsp(&ss);
3217                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
3218                                 LDAP_FREE(sval);
3219                                 if ( seen_obsolete ) {
3220                                         *code = LDAP_SCHERR_DUPOPT;
3221                                         *errp = ss;
3222                                         ldap_nameform_free(nf);
3223                                         return(NULL);
3224                                 }
3225                                 seen_obsolete = 1;
3226                                 nf->nf_obsolete = LDAP_SCHEMA_YES;
3227                                 parse_whsp(&ss);
3228                         } else if ( !strcasecmp(sval,"MUST") ) {
3229                                 LDAP_FREE(sval);
3230                                 if ( seen_must ) {
3231                                         *code = LDAP_SCHERR_DUPOPT;
3232                                         *errp = ss;
3233                                         ldap_nameform_free(nf);
3234                                         return(NULL);
3235                                 }
3236                                 seen_must = 1;
3237                                 nf->nf_at_oids_must = parse_oids(&ss,code,0);
3238                                 if ( !nf->nf_at_oids_must && *code != LDAP_SUCCESS ) {
3239                                         *errp = ss;
3240                                         ldap_nameform_free(nf);
3241                                         return NULL;
3242                                 }
3243                                 parse_whsp(&ss);
3244                         } else if ( !strcasecmp(sval,"MAY") ) {
3245                                 LDAP_FREE(sval);
3246                                 if ( seen_may ) {
3247                                         *code = LDAP_SCHERR_DUPOPT;
3248                                         *errp = ss;
3249                                         ldap_nameform_free(nf);
3250                                         return(NULL);
3251                                 }
3252                                 seen_may = 1;
3253                                 nf->nf_at_oids_may = parse_oids(&ss,code,0);
3254                                 if ( !nf->nf_at_oids_may && *code != LDAP_SUCCESS ) {
3255                                         *errp = ss;
3256                                         ldap_nameform_free(nf);
3257                                         return NULL;
3258                                 }
3259                                 parse_whsp(&ss);
3260                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
3261                                 /* Should be parse_qdstrings */
3262                                 ext_vals = parse_qdescrs(&ss, code);
3263                                 if ( !ext_vals ) {
3264                                         *errp = ss;
3265                                         ldap_nameform_free(nf);
3266                                         return NULL;
3267                                 }
3268                                 if ( add_extension(&nf->nf_extensions,
3269                                                     sval, ext_vals) ) {
3270                                         *code = LDAP_SCHERR_OUTOFMEM;
3271                                         *errp = ss;
3272                                         LDAP_FREE(sval);
3273                                         ldap_nameform_free(nf);
3274                                         return NULL;
3275                                 }
3276                         } else {
3277                                 *code = LDAP_SCHERR_UNEXPTOKEN;
3278                                 *errp = ss;
3279                                 LDAP_FREE(sval);
3280                                 ldap_nameform_free(nf);
3281                                 return NULL;
3282                         }
3283                         break;
3284                 default:
3285                         *code = LDAP_SCHERR_UNEXPTOKEN;
3286                         *errp = ss;
3287                         LDAP_FREE(sval);
3288                         ldap_nameform_free(nf);
3289                         return NULL;
3290                 }
3291         }
3292 }
3293
3294 static char *const err2text[] = {
3295         N_("Success"),
3296         N_("Out of memory"),
3297         N_("Unexpected token"),
3298         N_("Missing opening parenthesis"),
3299         N_("Missing closing parenthesis"),
3300         N_("Expecting digit"),
3301         N_("Expecting a name"),
3302         N_("Bad description"),
3303         N_("Bad superiors"),
3304         N_("Duplicate option"),
3305         N_("Unexpected end of data"),
3306         N_("Missing required field"),
3307         N_("Out of order field")
3308 };
3309
3310 char *
3311 ldap_scherr2str(int code)
3312 {
3313         if ( code < 0 || code >= (int)(sizeof(err2text)/sizeof(char *)) ) {
3314                 return _("Unknown error");
3315         } else {
3316                 return _(err2text[code]);
3317         }
3318 }