]> git.sur5r.net Git - openldap/blob - libraries/libldap/schema.c
Happy new year! (belated)
[openldap] / libraries / libldap / schema.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2008 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                         /* for suggested minimum upper bound on the number
1031                          * of characters (RFC 4517) */
1032                         **sp != '{' &&
1033                         **sp != '\0' )
1034                         (*sp)++;
1035                 q = *sp;
1036                 res = LDAP_MALLOC(q-p+1);
1037                 if ( !res ) {
1038                         kind = TK_OUTOFMEM;
1039                 } else {
1040                         strncpy(res,p,q-p);
1041                         res[q-p] = '\0';
1042                         *token_val = res;
1043                 }
1044                 break;
1045 /*              kind = TK_UNEXPCHAR; */
1046 /*              break; */
1047         }
1048         
1049         return kind;
1050 }
1051
1052 /* Gobble optional whitespace */
1053 static void
1054 parse_whsp(const char **sp)
1055 {
1056         while (LDAP_SPACE(**sp))
1057                 (*sp)++;
1058 }
1059
1060 /* TBC:!!
1061  * General note for all parsers: to guarantee the algorithm halts they
1062  * must always advance the pointer even when an error is found.  For
1063  * this one is not that important since an error here is fatal at the
1064  * upper layers, but it is a simple strategy that will not get in
1065  * endless loops.
1066  */
1067
1068 /* Parse a sequence of dot-separated decimal strings */
1069 char *
1070 ldap_int_parse_numericoid(const char **sp, int *code, const int flags)
1071 {
1072         char * res = NULL;
1073         const char * start = *sp;
1074         int len;
1075         int quoted = 0;
1076
1077         /* Netscape puts the SYNTAX value in quotes (incorrectly) */
1078         if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && **sp == '\'' ) {
1079                 quoted = 1;
1080                 (*sp)++;
1081                 start++;
1082         }
1083         /* Each iteration of this loop gets one decimal string */
1084         while (**sp) {
1085                 if ( !LDAP_DIGIT(**sp) ) {
1086                         /*
1087                          * Initial char is not a digit or char after dot is
1088                          * not a digit
1089                          */
1090                         *code = LDAP_SCHERR_NODIGIT;
1091                         return NULL;
1092                 }
1093                 (*sp)++;
1094                 while ( LDAP_DIGIT(**sp) )
1095                         (*sp)++;
1096                 if ( **sp != '.' )
1097                         break;
1098                 /* Otherwise, gobble the dot and loop again */
1099                 (*sp)++;
1100         }
1101         /* Now *sp points at the char past the numericoid. Perfect. */
1102         len = *sp - start;
1103         if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && quoted ) {
1104                 if ( **sp == '\'' ) {
1105                         (*sp)++;
1106                 } else {
1107                         *code = LDAP_SCHERR_UNEXPTOKEN;
1108                         return NULL;
1109                 }
1110         }
1111         if (flags & LDAP_SCHEMA_SKIP) {
1112                 res = (char *)start;
1113         } else {
1114                 res = LDAP_MALLOC(len+1);
1115                 if (!res) {
1116                         *code = LDAP_SCHERR_OUTOFMEM;
1117                         return(NULL);
1118                 }
1119                 strncpy(res,start,len);
1120                 res[len] = '\0';
1121         }
1122         return(res);
1123 }
1124
1125 /* Parse a sequence of dot-separated decimal strings */
1126 int
1127 ldap_int_parse_ruleid(const char **sp, int *code, const int flags, int *ruleid)
1128 {
1129         *ruleid=0;
1130
1131         if ( !LDAP_DIGIT(**sp) ) {
1132                 *code = LDAP_SCHERR_NODIGIT;
1133                 return -1;
1134         }
1135         *ruleid = (**sp) - '0';
1136         (*sp)++;
1137
1138         while ( LDAP_DIGIT(**sp) ) {
1139                 *ruleid *= 10;
1140                 *ruleid += (**sp) - '0';
1141                 (*sp)++;
1142         }
1143
1144         return 0;
1145 }
1146
1147 /* Parse a qdescr or a list of them enclosed in () */
1148 static char **
1149 parse_qdescrs(const char **sp, int *code)
1150 {
1151         char ** res;
1152         char ** res1;
1153         tk_t kind;
1154         char * sval;
1155         int size;
1156         int pos;
1157
1158         parse_whsp(sp);
1159         kind = get_token(sp,&sval);
1160         if ( kind == TK_LEFTPAREN ) {
1161                 /* Let's presume there will be at least 2 entries */
1162                 size = 3;
1163                 res = LDAP_CALLOC(3,sizeof(char *));
1164                 if ( !res ) {
1165                         *code = LDAP_SCHERR_OUTOFMEM;
1166                         return NULL;
1167                 }
1168                 pos = 0;
1169                 while (1) {
1170                         parse_whsp(sp);
1171                         kind = get_token(sp,&sval);
1172                         if ( kind == TK_RIGHTPAREN )
1173                                 break;
1174                         if ( kind == TK_QDESCR ) {
1175                                 if ( pos == size-2 ) {
1176                                         size++;
1177                                         res1 = LDAP_REALLOC(res,size*sizeof(char *));
1178                                         if ( !res1 ) {
1179                                                 LDAP_VFREE(res);
1180                                                 LDAP_FREE(sval);
1181                                                 *code = LDAP_SCHERR_OUTOFMEM;
1182                                                 return(NULL);
1183                                         }
1184                                         res = res1;
1185                                 }
1186                                 res[pos++] = sval;
1187                                 res[pos] = NULL;
1188                                 parse_whsp(sp);
1189                         } else {
1190                                 LDAP_VFREE(res);
1191                                 LDAP_FREE(sval);
1192                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1193                                 return(NULL);
1194                         }
1195                 }
1196                 parse_whsp(sp);
1197                 return(res);
1198         } else if ( kind == TK_QDESCR ) {
1199                 res = LDAP_CALLOC(2,sizeof(char *));
1200                 if ( !res ) {
1201                         *code = LDAP_SCHERR_OUTOFMEM;
1202                         return NULL;
1203                 }
1204                 res[0] = sval;
1205                 res[1] = NULL;
1206                 parse_whsp(sp);
1207                 return res;
1208         } else {
1209                 LDAP_FREE(sval);
1210                 *code = LDAP_SCHERR_BADNAME;
1211                 return NULL;
1212         }
1213 }
1214
1215 /* Parse a woid */
1216 static char *
1217 parse_woid(const char **sp, int *code)
1218 {
1219         char * sval;
1220         tk_t kind;
1221
1222         parse_whsp(sp);
1223         kind = get_token(sp, &sval);
1224         if ( kind != TK_BAREWORD ) {
1225                 LDAP_FREE(sval);
1226                 *code = LDAP_SCHERR_UNEXPTOKEN;
1227                 return NULL;
1228         }
1229         parse_whsp(sp);
1230         return sval;
1231 }
1232
1233 /* Parse a noidlen */
1234 static char *
1235 parse_noidlen(const char **sp, int *code, int *len, int flags)
1236 {
1237         char * sval;
1238         const char *savepos;
1239         int quoted = 0;
1240         int allow_quoted = ( flags & LDAP_SCHEMA_ALLOW_QUOTED );
1241         int allow_oidmacro = ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO );
1242
1243         *len = 0;
1244         /* Netscape puts the SYNTAX value in quotes (incorrectly) */
1245         if ( allow_quoted && **sp == '\'' ) {
1246                 quoted = 1;
1247                 (*sp)++;
1248         }
1249         savepos = *sp;
1250         sval = ldap_int_parse_numericoid(sp, code, 0);
1251         if ( !sval ) {
1252                 if ( allow_oidmacro
1253                         && *sp == savepos
1254                         && *code == LDAP_SCHERR_NODIGIT )
1255                 {
1256                         if ( get_token(sp, &sval) != TK_BAREWORD ) {
1257                                 if ( sval != NULL ) {
1258                                         LDAP_FREE(sval);
1259                                 }
1260                                 return NULL;
1261                         }
1262                 } else {
1263                         return NULL;
1264                 }
1265         }
1266         if ( **sp == '{' /*}*/ ) {
1267                 (*sp)++;
1268                 *len = atoi(*sp);
1269                 while ( LDAP_DIGIT(**sp) )
1270                         (*sp)++;
1271                 if ( **sp != /*{*/ '}' ) {
1272                         *code = LDAP_SCHERR_UNEXPTOKEN;
1273                         LDAP_FREE(sval);
1274                         return NULL;
1275                 }
1276                 (*sp)++;
1277         }               
1278         if ( allow_quoted && quoted ) {
1279                 if ( **sp == '\'' ) {
1280                         (*sp)++;
1281                 } else {
1282                         *code = LDAP_SCHERR_UNEXPTOKEN;
1283                         LDAP_FREE(sval);
1284                         return NULL;
1285                 }
1286         }
1287         return sval;
1288 }
1289
1290 /*
1291  * Next routine will accept a qdstring in place of an oid if
1292  * allow_quoted is set.  This is necessary to interoperate with
1293  * Netscape Directory server that will improperly quote each oid (at
1294  * least those of the descr kind) in the SUP clause.
1295  */
1296
1297 /* Parse a woid or a $-separated list of them enclosed in () */
1298 static char **
1299 parse_oids(const char **sp, int *code, const int allow_quoted)
1300 {
1301         char ** res;
1302         char ** res1;
1303         tk_t kind;
1304         char * sval;
1305         int size;
1306         int pos;
1307
1308         /*
1309          * Strictly speaking, doing this here accepts whsp before the
1310          * ( at the begining of an oidlist, but this is harmless.  Also,
1311          * we are very liberal in what we accept as an OID.  Maybe
1312          * refine later.
1313          */
1314         parse_whsp(sp);
1315         kind = get_token(sp,&sval);
1316         if ( kind == TK_LEFTPAREN ) {
1317                 /* Let's presume there will be at least 2 entries */
1318                 size = 3;
1319                 res = LDAP_CALLOC(3,sizeof(char *));
1320                 if ( !res ) {
1321                         *code = LDAP_SCHERR_OUTOFMEM;
1322                         return NULL;
1323                 }
1324                 pos = 0;
1325                 parse_whsp(sp);
1326                 kind = get_token(sp,&sval);
1327                 if ( kind == TK_BAREWORD ||
1328                      ( allow_quoted && kind == TK_QDSTRING ) ) {
1329                         res[pos++] = sval;
1330                         res[pos] = NULL;
1331                 } else if ( kind == TK_RIGHTPAREN ) {
1332                         /* FIXME: be liberal in what we accept... */
1333                         parse_whsp(sp);
1334                         LDAP_FREE(res);
1335                         return NULL;
1336                 } else {
1337                         *code = LDAP_SCHERR_UNEXPTOKEN;
1338                         LDAP_FREE(sval);
1339                         LDAP_VFREE(res);
1340                         return NULL;
1341                 }
1342                 parse_whsp(sp);
1343                 while (1) {
1344                         kind = get_token(sp,&sval);
1345                         if ( kind == TK_RIGHTPAREN )
1346                                 break;
1347                         if ( kind == TK_DOLLAR ) {
1348                                 parse_whsp(sp);
1349                                 kind = get_token(sp,&sval);
1350                                 if ( kind == TK_BAREWORD ||
1351                                      ( allow_quoted &&
1352                                        kind == TK_QDSTRING ) ) {
1353                                         if ( pos == size-2 ) {
1354                                                 size++;
1355                                                 res1 = LDAP_REALLOC(res,size*sizeof(char *));
1356                                                 if ( !res1 ) {
1357                                                         LDAP_FREE(sval);
1358                                                         LDAP_VFREE(res);
1359                                                         *code = LDAP_SCHERR_OUTOFMEM;
1360                                                         return(NULL);
1361                                                 }
1362                                                 res = res1;
1363                                         }
1364                                         res[pos++] = sval;
1365                                         res[pos] = NULL;
1366                                 } else {
1367                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1368                                         LDAP_FREE(sval);
1369                                         LDAP_VFREE(res);
1370                                         return NULL;
1371                                 }
1372                                 parse_whsp(sp);
1373                         } else {
1374                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1375                                 LDAP_FREE(sval);
1376                                 LDAP_VFREE(res);
1377                                 return NULL;
1378                         }
1379                 }
1380                 parse_whsp(sp);
1381                 return(res);
1382         } else if ( kind == TK_BAREWORD ||
1383                     ( allow_quoted && kind == TK_QDSTRING ) ) {
1384                 res = LDAP_CALLOC(2,sizeof(char *));
1385                 if ( !res ) {
1386                         LDAP_FREE(sval);
1387                         *code = LDAP_SCHERR_OUTOFMEM;
1388                         return NULL;
1389                 }
1390                 res[0] = sval;
1391                 res[1] = NULL;
1392                 parse_whsp(sp);
1393                 return res;
1394         } else {
1395                 LDAP_FREE(sval);
1396                 *code = LDAP_SCHERR_BADNAME;
1397                 return NULL;
1398         }
1399 }
1400
1401 static int
1402 add_extension(LDAPSchemaExtensionItem ***extensions,
1403               char * name, char ** values)
1404 {
1405         int n;
1406         LDAPSchemaExtensionItem **tmp, *ext;
1407
1408         ext = LDAP_CALLOC(1, sizeof(LDAPSchemaExtensionItem));
1409         if ( !ext )
1410                 return 1;
1411         ext->lsei_name = name;
1412         ext->lsei_values = values;
1413
1414         if ( !*extensions ) {
1415                 *extensions =
1416                   LDAP_CALLOC(2, sizeof(LDAPSchemaExtensionItem *));
1417                 if ( !*extensions ) {
1418                         LDAP_FREE( ext );
1419                         return 1;
1420                 }
1421                 n = 0;
1422         } else {
1423                 for ( n=0; (*extensions)[n] != NULL; n++ )
1424                         ;
1425                 tmp = LDAP_REALLOC(*extensions,
1426                                    (n+2)*sizeof(LDAPSchemaExtensionItem *));
1427                 if ( !tmp ) {
1428                         LDAP_FREE( ext );
1429                         return 1;
1430                 }
1431                 *extensions = tmp;
1432         }
1433         (*extensions)[n] = ext;
1434         (*extensions)[n+1] = NULL;
1435         return 0;
1436 }
1437
1438 static void
1439 free_extensions(LDAPSchemaExtensionItem **extensions)
1440 {
1441         LDAPSchemaExtensionItem **ext;
1442
1443         if ( extensions ) {
1444                 for ( ext = extensions; *ext != NULL; ext++ ) {
1445                         LDAP_FREE((*ext)->lsei_name);
1446                         LDAP_VFREE((*ext)->lsei_values);
1447                         LDAP_FREE(*ext);
1448                 }
1449                 LDAP_FREE(extensions);
1450         }
1451 }
1452
1453 void
1454 ldap_syntax_free( LDAPSyntax * syn )
1455 {
1456         LDAP_FREE(syn->syn_oid);
1457         if (syn->syn_names) LDAP_VFREE(syn->syn_names);
1458         if (syn->syn_desc) LDAP_FREE(syn->syn_desc);
1459         free_extensions(syn->syn_extensions);
1460         LDAP_FREE(syn);
1461 }
1462
1463 LDAPSyntax *
1464 ldap_str2syntax( LDAP_CONST char * s,
1465         int * code,
1466         LDAP_CONST char ** errp,
1467         LDAP_CONST unsigned flags )
1468 {
1469         tk_t kind;
1470         const char * ss = s;
1471         char * sval;
1472         int seen_name = 0;
1473         int seen_desc = 0;
1474         LDAPSyntax * syn;
1475         char ** ext_vals;
1476
1477         if ( !s ) {
1478                 *code = LDAP_SCHERR_EMPTY;
1479                 *errp = "";
1480                 return NULL;
1481         }
1482
1483         *errp = s;
1484         syn = LDAP_CALLOC(1,sizeof(LDAPSyntax));
1485
1486         if ( !syn ) {
1487                 *code = LDAP_SCHERR_OUTOFMEM;
1488                 return NULL;
1489         }
1490
1491         kind = get_token(&ss,&sval);
1492         if ( kind != TK_LEFTPAREN ) {
1493                 LDAP_FREE(sval);
1494                 *code = LDAP_SCHERR_NOLEFTPAREN;
1495                 ldap_syntax_free(syn);
1496                 return NULL;
1497         }
1498
1499         parse_whsp(&ss);
1500         syn->syn_oid = ldap_int_parse_numericoid(&ss,code,0);
1501         if ( !syn->syn_oid ) {
1502                 *errp = ss;
1503                 ldap_syntax_free(syn);
1504                 return NULL;
1505         }
1506         parse_whsp(&ss);
1507
1508         /*
1509          * Beyond this point we will be liberal and accept the items
1510          * in any order.
1511          */
1512         while (1) {
1513                 kind = get_token(&ss,&sval);
1514                 switch (kind) {
1515                 case TK_EOS:
1516                         *code = LDAP_SCHERR_NORIGHTPAREN;
1517                         *errp = ss;
1518                         ldap_syntax_free(syn);
1519                         return NULL;
1520                 case TK_RIGHTPAREN:
1521                         return syn;
1522                 case TK_BAREWORD:
1523                         if ( !strcasecmp(sval,"NAME") ) {
1524                                 LDAP_FREE(sval);
1525                                 if ( seen_name ) {
1526                                         *code = LDAP_SCHERR_DUPOPT;
1527                                         *errp = ss;
1528                                         ldap_syntax_free(syn);
1529                                         return(NULL);
1530                                 }
1531                                 seen_name = 1;
1532                                 syn->syn_names = parse_qdescrs(&ss,code);
1533                                 if ( !syn->syn_names ) {
1534                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1535                                                 *code = LDAP_SCHERR_BADNAME;
1536                                         *errp = ss;
1537                                         ldap_syntax_free(syn);
1538                                         return NULL;
1539                                 }
1540                         } else if ( !strcasecmp(sval,"DESC") ) {
1541                                 LDAP_FREE(sval);
1542                                 if ( seen_desc ) {
1543                                         *code = LDAP_SCHERR_DUPOPT;
1544                                         *errp = ss;
1545                                         ldap_syntax_free(syn);
1546                                         return(NULL);
1547                                 }
1548                                 seen_desc = 1;
1549                                 parse_whsp(&ss);
1550                                 kind = get_token(&ss,&sval);
1551                                 if ( kind != TK_QDSTRING ) {
1552                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1553                                         *errp = ss;
1554                                         LDAP_FREE(sval);
1555                                         ldap_syntax_free(syn);
1556                                         return NULL;
1557                                 }
1558                                 syn->syn_desc = sval;
1559                                 parse_whsp(&ss);
1560                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1561                                 /* Should be parse_qdstrings */
1562                                 ext_vals = parse_qdescrs(&ss, code);
1563                                 if ( !ext_vals ) {
1564                                         *errp = ss;
1565                                         ldap_syntax_free(syn);
1566                                         return NULL;
1567                                 }
1568                                 if ( add_extension(&syn->syn_extensions,
1569                                                     sval, ext_vals) ) {
1570                                         *code = LDAP_SCHERR_OUTOFMEM;
1571                                         *errp = ss;
1572                                         LDAP_FREE(sval);
1573                                         ldap_syntax_free(syn);
1574                                         return NULL;
1575                                 }
1576                         } else {
1577                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1578                                 *errp = ss;
1579                                 LDAP_FREE(sval);
1580                                 ldap_syntax_free(syn);
1581                                 return NULL;
1582                         }
1583                         break;
1584                 default:
1585                         *code = LDAP_SCHERR_UNEXPTOKEN;
1586                         *errp = ss;
1587                         LDAP_FREE(sval);
1588                         ldap_syntax_free(syn);
1589                         return NULL;
1590                 }
1591         }
1592 }
1593
1594 void
1595 ldap_matchingrule_free( LDAPMatchingRule * mr )
1596 {
1597         LDAP_FREE(mr->mr_oid);
1598         if (mr->mr_names) LDAP_VFREE(mr->mr_names);
1599         if (mr->mr_desc) LDAP_FREE(mr->mr_desc);
1600         if (mr->mr_syntax_oid) LDAP_FREE(mr->mr_syntax_oid);
1601         free_extensions(mr->mr_extensions);
1602         LDAP_FREE(mr);
1603 }
1604
1605 LDAPMatchingRule *
1606 ldap_str2matchingrule( LDAP_CONST char * s,
1607         int * code,
1608         LDAP_CONST char ** errp,
1609         LDAP_CONST unsigned flags )
1610 {
1611         tk_t kind;
1612         const char * ss = s;
1613         char * sval;
1614         int seen_name = 0;
1615         int seen_desc = 0;
1616         int seen_obsolete = 0;
1617         int seen_syntax = 0;
1618         LDAPMatchingRule * mr;
1619         char ** ext_vals;
1620         const char * savepos;
1621
1622         if ( !s ) {
1623                 *code = LDAP_SCHERR_EMPTY;
1624                 *errp = "";
1625                 return NULL;
1626         }
1627
1628         *errp = s;
1629         mr = LDAP_CALLOC(1,sizeof(LDAPMatchingRule));
1630
1631         if ( !mr ) {
1632                 *code = LDAP_SCHERR_OUTOFMEM;
1633                 return NULL;
1634         }
1635
1636         kind = get_token(&ss,&sval);
1637         if ( kind != TK_LEFTPAREN ) {
1638                 *code = LDAP_SCHERR_NOLEFTPAREN;
1639                 LDAP_FREE(sval);
1640                 ldap_matchingrule_free(mr);
1641                 return NULL;
1642         }
1643
1644         parse_whsp(&ss);
1645         savepos = ss;
1646         mr->mr_oid = ldap_int_parse_numericoid(&ss,code,flags);
1647         if ( !mr->mr_oid ) {
1648                 if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
1649                         /* Backtracking */
1650                         ss = savepos;
1651                         kind = get_token(&ss,&sval);
1652                         if ( kind == TK_BAREWORD ) {
1653                                 if ( !strcasecmp(sval, "NAME") ||
1654                                      !strcasecmp(sval, "DESC") ||
1655                                      !strcasecmp(sval, "OBSOLETE") ||
1656                                      !strcasecmp(sval, "SYNTAX") ||
1657                                      !strncasecmp(sval, "X-", 2) ) {
1658                                         /* Missing OID, backtrack */
1659                                         ss = savepos;
1660                                 } else {
1661                                         /* Non-numerical OID, ignore */
1662                                 }
1663                         }
1664                         LDAP_FREE(sval);
1665                 } else {
1666                         *errp = ss;
1667                         ldap_matchingrule_free(mr);
1668                         return NULL;
1669                 }
1670         }
1671         parse_whsp(&ss);
1672
1673         /*
1674          * Beyond this point we will be liberal and accept the items
1675          * in any order.
1676          */
1677         while (1) {
1678                 kind = get_token(&ss,&sval);
1679                 switch (kind) {
1680                 case TK_EOS:
1681                         *code = LDAP_SCHERR_NORIGHTPAREN;
1682                         *errp = ss;
1683                         ldap_matchingrule_free(mr);
1684                         return NULL;
1685                 case TK_RIGHTPAREN:
1686                         if( !seen_syntax ) {
1687                                 *code = LDAP_SCHERR_MISSING;
1688                                 ldap_matchingrule_free(mr);
1689                                 return NULL;
1690                         }
1691                         return mr;
1692                 case TK_BAREWORD:
1693                         if ( !strcasecmp(sval,"NAME") ) {
1694                                 LDAP_FREE(sval);
1695                                 if ( seen_name ) {
1696                                         *code = LDAP_SCHERR_DUPOPT;
1697                                         *errp = ss;
1698                                         ldap_matchingrule_free(mr);
1699                                         return(NULL);
1700                                 }
1701                                 seen_name = 1;
1702                                 mr->mr_names = parse_qdescrs(&ss,code);
1703                                 if ( !mr->mr_names ) {
1704                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1705                                                 *code = LDAP_SCHERR_BADNAME;
1706                                         *errp = ss;
1707                                         ldap_matchingrule_free(mr);
1708                                         return NULL;
1709                                 }
1710                         } else if ( !strcasecmp(sval,"DESC") ) {
1711                                 LDAP_FREE(sval);
1712                                 if ( seen_desc ) {
1713                                         *code = LDAP_SCHERR_DUPOPT;
1714                                         *errp = ss;
1715                                         ldap_matchingrule_free(mr);
1716                                         return(NULL);
1717                                 }
1718                                 seen_desc = 1;
1719                                 parse_whsp(&ss);
1720                                 kind = get_token(&ss,&sval);
1721                                 if ( kind != TK_QDSTRING ) {
1722                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1723                                         *errp = ss;
1724                                         LDAP_FREE(sval);
1725                                         ldap_matchingrule_free(mr);
1726                                         return NULL;
1727                                 }
1728                                 mr->mr_desc = sval;
1729                                 parse_whsp(&ss);
1730                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
1731                                 LDAP_FREE(sval);
1732                                 if ( seen_obsolete ) {
1733                                         *code = LDAP_SCHERR_DUPOPT;
1734                                         *errp = ss;
1735                                         ldap_matchingrule_free(mr);
1736                                         return(NULL);
1737                                 }
1738                                 seen_obsolete = 1;
1739                                 mr->mr_obsolete = LDAP_SCHEMA_YES;
1740                                 parse_whsp(&ss);
1741                         } else if ( !strcasecmp(sval,"SYNTAX") ) {
1742                                 LDAP_FREE(sval);
1743                                 if ( seen_syntax ) {
1744                                         *code = LDAP_SCHERR_DUPOPT;
1745                                         *errp = ss;
1746                                         ldap_matchingrule_free(mr);
1747                                         return(NULL);
1748                                 }
1749                                 seen_syntax = 1;
1750                                 parse_whsp(&ss);
1751                                 mr->mr_syntax_oid =
1752                                         ldap_int_parse_numericoid(&ss,code,flags);
1753                                 if ( !mr->mr_syntax_oid ) {
1754                                         *errp = ss;
1755                                         ldap_matchingrule_free(mr);
1756                                         return NULL;
1757                                 }
1758                                 parse_whsp(&ss);
1759                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1760                                 /* Should be parse_qdstrings */
1761                                 ext_vals = parse_qdescrs(&ss, code);
1762                                 if ( !ext_vals ) {
1763                                         *errp = ss;
1764                                         ldap_matchingrule_free(mr);
1765                                         return NULL;
1766                                 }
1767                                 if ( add_extension(&mr->mr_extensions,
1768                                                     sval, ext_vals) ) {
1769                                         *code = LDAP_SCHERR_OUTOFMEM;
1770                                         *errp = ss;
1771                                         LDAP_FREE(sval);
1772                                         ldap_matchingrule_free(mr);
1773                                         return NULL;
1774                                 }
1775                         } else {
1776                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1777                                 *errp = ss;
1778                                 LDAP_FREE(sval);
1779                                 ldap_matchingrule_free(mr);
1780                                 return NULL;
1781                         }
1782                         break;
1783                 default:
1784                         *code = LDAP_SCHERR_UNEXPTOKEN;
1785                         *errp = ss;
1786                         LDAP_FREE(sval);
1787                         ldap_matchingrule_free(mr);
1788                         return NULL;
1789                 }
1790         }
1791 }
1792
1793 void
1794 ldap_matchingruleuse_free( LDAPMatchingRuleUse * mru )
1795 {
1796         LDAP_FREE(mru->mru_oid);
1797         if (mru->mru_names) LDAP_VFREE(mru->mru_names);
1798         if (mru->mru_desc) LDAP_FREE(mru->mru_desc);
1799         if (mru->mru_applies_oids) LDAP_VFREE(mru->mru_applies_oids);
1800         free_extensions(mru->mru_extensions);
1801         LDAP_FREE(mru);
1802 }
1803
1804 LDAPMatchingRuleUse *
1805 ldap_str2matchingruleuse( LDAP_CONST char * s,
1806         int * code,
1807         LDAP_CONST char ** errp,
1808         LDAP_CONST unsigned flags )
1809 {
1810         tk_t kind;
1811         const char * ss = s;
1812         char * sval;
1813         int seen_name = 0;
1814         int seen_desc = 0;
1815         int seen_obsolete = 0;
1816         int seen_applies = 0;
1817         LDAPMatchingRuleUse * mru;
1818         char ** ext_vals;
1819         const char * savepos;
1820
1821         if ( !s ) {
1822                 *code = LDAP_SCHERR_EMPTY;
1823                 *errp = "";
1824                 return NULL;
1825         }
1826
1827         *errp = s;
1828         mru = LDAP_CALLOC(1,sizeof(LDAPMatchingRuleUse));
1829
1830         if ( !mru ) {
1831                 *code = LDAP_SCHERR_OUTOFMEM;
1832                 return NULL;
1833         }
1834
1835         kind = get_token(&ss,&sval);
1836         if ( kind != TK_LEFTPAREN ) {
1837                 *code = LDAP_SCHERR_NOLEFTPAREN;
1838                 LDAP_FREE(sval);
1839                 ldap_matchingruleuse_free(mru);
1840                 return NULL;
1841         }
1842
1843         parse_whsp(&ss);
1844         savepos = ss;
1845         mru->mru_oid = ldap_int_parse_numericoid(&ss,code,flags);
1846         if ( !mru->mru_oid ) {
1847                 if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
1848                         /* Backtracking */
1849                         ss = savepos;
1850                         kind = get_token(&ss,&sval);
1851                         if ( kind == TK_BAREWORD ) {
1852                                 if ( !strcasecmp(sval, "NAME") ||
1853                                      !strcasecmp(sval, "DESC") ||
1854                                      !strcasecmp(sval, "OBSOLETE") ||
1855                                      !strcasecmp(sval, "APPLIES") ||
1856                                      !strncasecmp(sval, "X-", 2) ) {
1857                                         /* Missing OID, backtrack */
1858                                         ss = savepos;
1859                                 } else {
1860                                         /* Non-numerical OID, ignore */
1861                                 }
1862                         }
1863                         LDAP_FREE(sval);
1864                 } else {
1865                         *errp = ss;
1866                         ldap_matchingruleuse_free(mru);
1867                         return NULL;
1868                 }
1869         }
1870         parse_whsp(&ss);
1871
1872         /*
1873          * Beyond this point we will be liberal and accept the items
1874          * in any order.
1875          */
1876         while (1) {
1877                 kind = get_token(&ss,&sval);
1878                 switch (kind) {
1879                 case TK_EOS:
1880                         *code = LDAP_SCHERR_NORIGHTPAREN;
1881                         *errp = ss;
1882                         ldap_matchingruleuse_free(mru);
1883                         return NULL;
1884                 case TK_RIGHTPAREN:
1885                         if( !seen_applies ) {
1886                                 *code = LDAP_SCHERR_MISSING;
1887                                 ldap_matchingruleuse_free(mru);
1888                                 return NULL;
1889                         }
1890                         return mru;
1891                 case TK_BAREWORD:
1892                         if ( !strcasecmp(sval,"NAME") ) {
1893                                 LDAP_FREE(sval);
1894                                 if ( seen_name ) {
1895                                         *code = LDAP_SCHERR_DUPOPT;
1896                                         *errp = ss;
1897                                         ldap_matchingruleuse_free(mru);
1898                                         return(NULL);
1899                                 }
1900                                 seen_name = 1;
1901                                 mru->mru_names = parse_qdescrs(&ss,code);
1902                                 if ( !mru->mru_names ) {
1903                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
1904                                                 *code = LDAP_SCHERR_BADNAME;
1905                                         *errp = ss;
1906                                         ldap_matchingruleuse_free(mru);
1907                                         return NULL;
1908                                 }
1909                         } else if ( !strcasecmp(sval,"DESC") ) {
1910                                 LDAP_FREE(sval);
1911                                 if ( seen_desc ) {
1912                                         *code = LDAP_SCHERR_DUPOPT;
1913                                         *errp = ss;
1914                                         ldap_matchingruleuse_free(mru);
1915                                         return(NULL);
1916                                 }
1917                                 seen_desc = 1;
1918                                 parse_whsp(&ss);
1919                                 kind = get_token(&ss,&sval);
1920                                 if ( kind != TK_QDSTRING ) {
1921                                         *code = LDAP_SCHERR_UNEXPTOKEN;
1922                                         *errp = ss;
1923                                         LDAP_FREE(sval);
1924                                         ldap_matchingruleuse_free(mru);
1925                                         return NULL;
1926                                 }
1927                                 mru->mru_desc = sval;
1928                                 parse_whsp(&ss);
1929                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
1930                                 LDAP_FREE(sval);
1931                                 if ( seen_obsolete ) {
1932                                         *code = LDAP_SCHERR_DUPOPT;
1933                                         *errp = ss;
1934                                         ldap_matchingruleuse_free(mru);
1935                                         return(NULL);
1936                                 }
1937                                 seen_obsolete = 1;
1938                                 mru->mru_obsolete = LDAP_SCHEMA_YES;
1939                                 parse_whsp(&ss);
1940                         } else if ( !strcasecmp(sval,"APPLIES") ) {
1941                                 LDAP_FREE(sval);
1942                                 if ( seen_applies ) {
1943                                         *code = LDAP_SCHERR_DUPOPT;
1944                                         *errp = ss;
1945                                         ldap_matchingruleuse_free(mru);
1946                                         return(NULL);
1947                                 }
1948                                 seen_applies = 1;
1949                                 mru->mru_applies_oids = parse_oids(&ss,
1950                                                              code,
1951                                                              flags);
1952                                 if ( !mru->mru_applies_oids && *code != LDAP_SUCCESS ) {
1953                                         *errp = ss;
1954                                         ldap_matchingruleuse_free(mru);
1955                                         return NULL;
1956                                 }
1957                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
1958                                 /* Should be parse_qdstrings */
1959                                 ext_vals = parse_qdescrs(&ss, code);
1960                                 if ( !ext_vals ) {
1961                                         *errp = ss;
1962                                         ldap_matchingruleuse_free(mru);
1963                                         return NULL;
1964                                 }
1965                                 if ( add_extension(&mru->mru_extensions,
1966                                                     sval, ext_vals) ) {
1967                                         *code = LDAP_SCHERR_OUTOFMEM;
1968                                         *errp = ss;
1969                                         LDAP_FREE(sval);
1970                                         ldap_matchingruleuse_free(mru);
1971                                         return NULL;
1972                                 }
1973                         } else {
1974                                 *code = LDAP_SCHERR_UNEXPTOKEN;
1975                                 *errp = ss;
1976                                 LDAP_FREE(sval);
1977                                 ldap_matchingruleuse_free(mru);
1978                                 return NULL;
1979                         }
1980                         break;
1981                 default:
1982                         *code = LDAP_SCHERR_UNEXPTOKEN;
1983                         *errp = ss;
1984                         LDAP_FREE(sval);
1985                         ldap_matchingruleuse_free(mru);
1986                         return NULL;
1987                 }
1988         }
1989 }
1990
1991 void
1992 ldap_attributetype_free(LDAPAttributeType * at)
1993 {
1994         LDAP_FREE(at->at_oid);
1995         if (at->at_names) LDAP_VFREE(at->at_names);
1996         if (at->at_desc) LDAP_FREE(at->at_desc);
1997         if (at->at_sup_oid) LDAP_FREE(at->at_sup_oid);
1998         if (at->at_equality_oid) LDAP_FREE(at->at_equality_oid);
1999         if (at->at_ordering_oid) LDAP_FREE(at->at_ordering_oid);
2000         if (at->at_substr_oid) LDAP_FREE(at->at_substr_oid);
2001         if (at->at_syntax_oid) LDAP_FREE(at->at_syntax_oid);
2002         free_extensions(at->at_extensions);
2003         LDAP_FREE(at);
2004 }
2005
2006 LDAPAttributeType *
2007 ldap_str2attributetype( LDAP_CONST char * s,
2008         int * code,
2009         LDAP_CONST char ** errp,
2010         LDAP_CONST unsigned flags )
2011 {
2012         tk_t kind;
2013         const char * ss = s;
2014         char * sval;
2015         int seen_name = 0;
2016         int seen_desc = 0;
2017         int seen_obsolete = 0;
2018         int seen_sup = 0;
2019         int seen_equality = 0;
2020         int seen_ordering = 0;
2021         int seen_substr = 0;
2022         int seen_syntax = 0;
2023         int seen_usage = 0;
2024         LDAPAttributeType * at;
2025         char ** ext_vals;
2026         const char * savepos;
2027
2028         if ( !s ) {
2029                 *code = LDAP_SCHERR_EMPTY;
2030                 *errp = "";
2031                 return NULL;
2032         }
2033
2034         *errp = s;
2035         at = LDAP_CALLOC(1,sizeof(LDAPAttributeType));
2036
2037         if ( !at ) {
2038                 *code = LDAP_SCHERR_OUTOFMEM;
2039                 return NULL;
2040         }
2041
2042         kind = get_token(&ss,&sval);
2043         if ( kind != TK_LEFTPAREN ) {
2044                 *code = LDAP_SCHERR_NOLEFTPAREN;
2045                 LDAP_FREE(sval);
2046                 ldap_attributetype_free(at);
2047                 return NULL;
2048         }
2049
2050         /*
2051          * Definitions MUST begin with an OID in the numericoid format.
2052          * However, this routine is used by clients to parse the response
2053          * from servers and very well known servers will provide an OID
2054          * in the wrong format or even no OID at all.  We do our best to
2055          * extract info from those servers.
2056          */
2057         parse_whsp(&ss);
2058         savepos = ss;
2059         at->at_oid = ldap_int_parse_numericoid(&ss,code,0);
2060         if ( !at->at_oid ) {
2061                 if ( ( flags & ( LDAP_SCHEMA_ALLOW_NO_OID
2062                                 | LDAP_SCHEMA_ALLOW_OID_MACRO ) )
2063                             && (ss == savepos) )
2064                 {
2065                         /* Backtracking */
2066                         ss = savepos;
2067                         kind = get_token(&ss,&sval);
2068                         if ( kind == TK_BAREWORD ) {
2069                                 if ( !strcasecmp(sval, "NAME") ||
2070                                      !strcasecmp(sval, "DESC") ||
2071                                      !strcasecmp(sval, "OBSOLETE") ||
2072                                      !strcasecmp(sval, "SUP") ||
2073                                      !strcasecmp(sval, "EQUALITY") ||
2074                                      !strcasecmp(sval, "ORDERING") ||
2075                                      !strcasecmp(sval, "SUBSTR") ||
2076                                      !strcasecmp(sval, "SYNTAX") ||
2077                                      !strcasecmp(sval, "SINGLE-VALUE") ||
2078                                      !strcasecmp(sval, "COLLECTIVE") ||
2079                                      !strcasecmp(sval, "NO-USER-MODIFICATION") ||
2080                                      !strcasecmp(sval, "USAGE") ||
2081                                      !strncasecmp(sval, "X-", 2) )
2082                                 {
2083                                         /* Missing OID, backtrack */
2084                                         ss = savepos;
2085                                 } else if ( flags
2086                                         & LDAP_SCHEMA_ALLOW_OID_MACRO)
2087                                 {
2088                                         /* Non-numerical OID ... */
2089                                         int len = ss-savepos;
2090                                         at->at_oid = LDAP_MALLOC(len+1);
2091                                         strncpy(at->at_oid, savepos, len);
2092                                         at->at_oid[len] = 0;
2093                                 }
2094                         }
2095                         LDAP_FREE(sval);
2096                 } else {
2097                         *errp = ss;
2098                         ldap_attributetype_free(at);
2099                         return NULL;
2100                 }
2101         }
2102         parse_whsp(&ss);
2103
2104         /*
2105          * Beyond this point we will be liberal and accept the items
2106          * in any order.
2107          */
2108         while (1) {
2109                 kind = get_token(&ss,&sval);
2110                 switch (kind) {
2111                 case TK_EOS:
2112                         *code = LDAP_SCHERR_NORIGHTPAREN;
2113                         *errp = ss;
2114                         ldap_attributetype_free(at);
2115                         return NULL;
2116                 case TK_RIGHTPAREN:
2117                         return at;
2118                 case TK_BAREWORD:
2119                         if ( !strcasecmp(sval,"NAME") ) {
2120                                 LDAP_FREE(sval);
2121                                 if ( seen_name ) {
2122                                         *code = LDAP_SCHERR_DUPOPT;
2123                                         *errp = ss;
2124                                         ldap_attributetype_free(at);
2125                                         return(NULL);
2126                                 }
2127                                 seen_name = 1;
2128                                 at->at_names = parse_qdescrs(&ss,code);
2129                                 if ( !at->at_names ) {
2130                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
2131                                                 *code = LDAP_SCHERR_BADNAME;
2132                                         *errp = ss;
2133                                         ldap_attributetype_free(at);
2134                                         return NULL;
2135                                 }
2136                         } else if ( !strcasecmp(sval,"DESC") ) {
2137                                 LDAP_FREE(sval);
2138                                 if ( seen_desc ) {
2139                                         *code = LDAP_SCHERR_DUPOPT;
2140                                         *errp = ss;
2141                                         ldap_attributetype_free(at);
2142                                         return(NULL);
2143                                 }
2144                                 seen_desc = 1;
2145                                 parse_whsp(&ss);
2146                                 kind = get_token(&ss,&sval);
2147                                 if ( kind != TK_QDSTRING ) {
2148                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2149                                         *errp = ss;
2150                                         LDAP_FREE(sval);
2151                                         ldap_attributetype_free(at);
2152                                         return NULL;
2153                                 }
2154                                 at->at_desc = sval;
2155                                 parse_whsp(&ss);
2156                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
2157                                 LDAP_FREE(sval);
2158                                 if ( seen_obsolete ) {
2159                                         *code = LDAP_SCHERR_DUPOPT;
2160                                         *errp = ss;
2161                                         ldap_attributetype_free(at);
2162                                         return(NULL);
2163                                 }
2164                                 seen_obsolete = 1;
2165                                 at->at_obsolete = LDAP_SCHEMA_YES;
2166                                 parse_whsp(&ss);
2167                         } else if ( !strcasecmp(sval,"SUP") ) {
2168                                 LDAP_FREE(sval);
2169                                 if ( seen_sup ) {
2170                                         *code = LDAP_SCHERR_DUPOPT;
2171                                         *errp = ss;
2172                                         ldap_attributetype_free(at);
2173                                         return(NULL);
2174                                 }
2175                                 seen_sup = 1;
2176                                 at->at_sup_oid = parse_woid(&ss,code);
2177                                 if ( !at->at_sup_oid ) {
2178                                         *errp = ss;
2179                                         ldap_attributetype_free(at);
2180                                         return NULL;
2181                                 }
2182                         } else if ( !strcasecmp(sval,"EQUALITY") ) {
2183                                 LDAP_FREE(sval);
2184                                 if ( seen_equality ) {
2185                                         *code = LDAP_SCHERR_DUPOPT;
2186                                         *errp = ss;
2187                                         ldap_attributetype_free(at);
2188                                         return(NULL);
2189                                 }
2190                                 seen_equality = 1;
2191                                 at->at_equality_oid = parse_woid(&ss,code);
2192                                 if ( !at->at_equality_oid ) {
2193                                         *errp = ss;
2194                                         ldap_attributetype_free(at);
2195                                         return NULL;
2196                                 }
2197                         } else if ( !strcasecmp(sval,"ORDERING") ) {
2198                                 LDAP_FREE(sval);
2199                                 if ( seen_ordering ) {
2200                                         *code = LDAP_SCHERR_DUPOPT;
2201                                         *errp = ss;
2202                                         ldap_attributetype_free(at);
2203                                         return(NULL);
2204                                 }
2205                                 seen_ordering = 1;
2206                                 at->at_ordering_oid = parse_woid(&ss,code);
2207                                 if ( !at->at_ordering_oid ) {
2208                                         *errp = ss;
2209                                         ldap_attributetype_free(at);
2210                                         return NULL;
2211                                 }
2212                         } else if ( !strcasecmp(sval,"SUBSTR") ) {
2213                                 LDAP_FREE(sval);
2214                                 if ( seen_substr ) {
2215                                         *code = LDAP_SCHERR_DUPOPT;
2216                                         *errp = ss;
2217                                         ldap_attributetype_free(at);
2218                                         return(NULL);
2219                                 }
2220                                 seen_substr = 1;
2221                                 at->at_substr_oid = parse_woid(&ss,code);
2222                                 if ( !at->at_substr_oid ) {
2223                                         *errp = ss;
2224                                         ldap_attributetype_free(at);
2225                                         return NULL;
2226                                 }
2227                         } else if ( !strcasecmp(sval,"SYNTAX") ) {
2228                                 LDAP_FREE(sval);
2229                                 if ( seen_syntax ) {
2230                                         *code = LDAP_SCHERR_DUPOPT;
2231                                         *errp = ss;
2232                                         ldap_attributetype_free(at);
2233                                         return(NULL);
2234                                 }
2235                                 seen_syntax = 1;
2236                                 parse_whsp(&ss);
2237                                 savepos = ss;
2238                                 at->at_syntax_oid =
2239                                         parse_noidlen(&ss,
2240                                                       code,
2241                                                       &at->at_syntax_len,
2242                                                       flags);
2243                                 if ( !at->at_syntax_oid ) {
2244                                     if ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO ) {
2245                                         kind = get_token(&ss,&sval);
2246                                         if (kind == TK_BAREWORD)
2247                                         {
2248                                             char *sp = strchr(sval, '{');
2249                                             at->at_syntax_oid = sval;
2250                                             if (sp)
2251                                             {
2252                                                 *sp++ = 0;
2253                                                 at->at_syntax_len = atoi(sp);
2254                                                 while ( LDAP_DIGIT(*sp) )
2255                                                         sp++;
2256                                                 if ( *sp != '}' ) {
2257                                                     *code = LDAP_SCHERR_UNEXPTOKEN;
2258                                                     *errp = ss;
2259                                                     ldap_attributetype_free(at);
2260                                                     return NULL;
2261                                                 }
2262                                             }
2263                                         }
2264                                     } else {
2265                                         *errp = ss;
2266                                         ldap_attributetype_free(at);
2267                                         return NULL;
2268                                     }
2269                                 }
2270                                 parse_whsp(&ss);
2271                         } else if ( !strcasecmp(sval,"SINGLE-VALUE") ) {
2272                                 LDAP_FREE(sval);
2273                                 if ( at->at_single_value ) {
2274                                         *code = LDAP_SCHERR_DUPOPT;
2275                                         *errp = ss;
2276                                         ldap_attributetype_free(at);
2277                                         return(NULL);
2278                                 }
2279                                 at->at_single_value = LDAP_SCHEMA_YES;
2280                                 parse_whsp(&ss);
2281                         } else if ( !strcasecmp(sval,"COLLECTIVE") ) {
2282                                 LDAP_FREE(sval);
2283                                 if ( at->at_collective ) {
2284                                         *code = LDAP_SCHERR_DUPOPT;
2285                                         *errp = ss;
2286                                         ldap_attributetype_free(at);
2287                                         return(NULL);
2288                                 }
2289                                 at->at_collective = LDAP_SCHEMA_YES;
2290                                 parse_whsp(&ss);
2291                         } else if ( !strcasecmp(sval,"NO-USER-MODIFICATION") ) {
2292                                 LDAP_FREE(sval);
2293                                 if ( at->at_no_user_mod ) {
2294                                         *code = LDAP_SCHERR_DUPOPT;
2295                                         *errp = ss;
2296                                         ldap_attributetype_free(at);
2297                                         return(NULL);
2298                                 }
2299                                 at->at_no_user_mod = LDAP_SCHEMA_YES;
2300                                 parse_whsp(&ss);
2301                         } else if ( !strcasecmp(sval,"USAGE") ) {
2302                                 LDAP_FREE(sval);
2303                                 if ( seen_usage ) {
2304                                         *code = LDAP_SCHERR_DUPOPT;
2305                                         *errp = ss;
2306                                         ldap_attributetype_free(at);
2307                                         return(NULL);
2308                                 }
2309                                 seen_usage = 1;
2310                                 parse_whsp(&ss);
2311                                 kind = get_token(&ss,&sval);
2312                                 if ( kind != TK_BAREWORD ) {
2313                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2314                                         *errp = ss;
2315                                         LDAP_FREE(sval);
2316                                         ldap_attributetype_free(at);
2317                                         return NULL;
2318                                 }
2319                                 if ( !strcasecmp(sval,"userApplications") )
2320                                         at->at_usage =
2321                                             LDAP_SCHEMA_USER_APPLICATIONS;
2322                                 else if ( !strcasecmp(sval,"directoryOperation") )
2323                                         at->at_usage =
2324                                             LDAP_SCHEMA_DIRECTORY_OPERATION;
2325                                 else if ( !strcasecmp(sval,"distributedOperation") )
2326                                         at->at_usage =
2327                                             LDAP_SCHEMA_DISTRIBUTED_OPERATION;
2328                                 else if ( !strcasecmp(sval,"dSAOperation") )
2329                                         at->at_usage =
2330                                             LDAP_SCHEMA_DSA_OPERATION;
2331                                 else {
2332                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2333                                         *errp = ss;
2334                                         LDAP_FREE(sval);
2335                                         ldap_attributetype_free(at);
2336                                         return NULL;
2337                                 }
2338                                 LDAP_FREE(sval);
2339                                 parse_whsp(&ss);
2340                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
2341                                 /* Should be parse_qdstrings */
2342                                 ext_vals = parse_qdescrs(&ss, code);
2343                                 if ( !ext_vals ) {
2344                                         *errp = ss;
2345                                         ldap_attributetype_free(at);
2346                                         return NULL;
2347                                 }
2348                                 if ( add_extension(&at->at_extensions,
2349                                                     sval, ext_vals) ) {
2350                                         *code = LDAP_SCHERR_OUTOFMEM;
2351                                         *errp = ss;
2352                                         LDAP_FREE(sval);
2353                                         ldap_attributetype_free(at);
2354                                         return NULL;
2355                                 }
2356                         } else {
2357                                 *code = LDAP_SCHERR_UNEXPTOKEN;
2358                                 *errp = ss;
2359                                 LDAP_FREE(sval);
2360                                 ldap_attributetype_free(at);
2361                                 return NULL;
2362                         }
2363                         break;
2364                 default:
2365                         *code = LDAP_SCHERR_UNEXPTOKEN;
2366                         *errp = ss;
2367                         LDAP_FREE(sval);
2368                         ldap_attributetype_free(at);
2369                         return NULL;
2370                 }
2371         }
2372 }
2373
2374 void
2375 ldap_objectclass_free(LDAPObjectClass * oc)
2376 {
2377         LDAP_FREE(oc->oc_oid);
2378         if (oc->oc_names) LDAP_VFREE(oc->oc_names);
2379         if (oc->oc_desc) LDAP_FREE(oc->oc_desc);
2380         if (oc->oc_sup_oids) LDAP_VFREE(oc->oc_sup_oids);
2381         if (oc->oc_at_oids_must) LDAP_VFREE(oc->oc_at_oids_must);
2382         if (oc->oc_at_oids_may) LDAP_VFREE(oc->oc_at_oids_may);
2383         free_extensions(oc->oc_extensions);
2384         LDAP_FREE(oc);
2385 }
2386
2387 LDAPObjectClass *
2388 ldap_str2objectclass( LDAP_CONST char * s,
2389         int * code,
2390         LDAP_CONST char ** errp,
2391         LDAP_CONST unsigned flags )
2392 {
2393         tk_t kind;
2394         const char * ss = s;
2395         char * sval;
2396         int seen_name = 0;
2397         int seen_desc = 0;
2398         int seen_obsolete = 0;
2399         int seen_sup = 0;
2400         int seen_kind = 0;
2401         int seen_must = 0;
2402         int seen_may = 0;
2403         LDAPObjectClass * oc;
2404         char ** ext_vals;
2405         const char * savepos;
2406
2407         if ( !s ) {
2408                 *code = LDAP_SCHERR_EMPTY;
2409                 *errp = "";
2410                 return NULL;
2411         }
2412
2413         *errp = s;
2414         oc = LDAP_CALLOC(1,sizeof(LDAPObjectClass));
2415
2416         if ( !oc ) {
2417                 *code = LDAP_SCHERR_OUTOFMEM;
2418                 return NULL;
2419         }
2420         oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
2421
2422         kind = get_token(&ss,&sval);
2423         if ( kind != TK_LEFTPAREN ) {
2424                 *code = LDAP_SCHERR_NOLEFTPAREN;
2425                 LDAP_FREE(sval);
2426                 ldap_objectclass_free(oc);
2427                 return NULL;
2428         }
2429
2430         /*
2431          * Definitions MUST begin with an OID in the numericoid format.
2432          * However, this routine is used by clients to parse the response
2433          * from servers and very well known servers will provide an OID
2434          * in the wrong format or even no OID at all.  We do our best to
2435          * extract info from those servers.
2436          */
2437         parse_whsp(&ss);
2438         savepos = ss;
2439         oc->oc_oid = ldap_int_parse_numericoid(&ss,code,0);
2440         if ( !oc->oc_oid ) {
2441                 if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) {
2442                         /* Backtracking */
2443                         ss = savepos;
2444                         kind = get_token(&ss,&sval);
2445                         if ( kind == TK_BAREWORD ) {
2446                                 if ( !strcasecmp(sval, "NAME") ||
2447                                      !strcasecmp(sval, "DESC") ||
2448                                      !strcasecmp(sval, "OBSOLETE") ||
2449                                      !strcasecmp(sval, "SUP") ||
2450                                      !strcasecmp(sval, "ABSTRACT") ||
2451                                      !strcasecmp(sval, "STRUCTURAL") ||
2452                                      !strcasecmp(sval, "AUXILIARY") ||
2453                                      !strcasecmp(sval, "MUST") ||
2454                                      !strcasecmp(sval, "MAY") ||
2455                                      !strncasecmp(sval, "X-", 2) ) {
2456                                         /* Missing OID, backtrack */
2457                                         ss = savepos;
2458                                 } else if ( flags &
2459                                         LDAP_SCHEMA_ALLOW_OID_MACRO ) {
2460                                         /* Non-numerical OID, ignore */
2461                                         int len = ss-savepos;
2462                                         oc->oc_oid = LDAP_MALLOC(len+1);
2463                                         strncpy(oc->oc_oid, savepos, len);
2464                                         oc->oc_oid[len] = 0;
2465                                 }
2466                         }
2467                         LDAP_FREE(sval);
2468                         *code = 0;
2469                 } else {
2470                         *errp = ss;
2471                         ldap_objectclass_free(oc);
2472                         return NULL;
2473                 }
2474         }
2475         parse_whsp(&ss);
2476
2477         /*
2478          * Beyond this point we will be liberal an accept the items
2479          * in any order.
2480          */
2481         while (1) {
2482                 kind = get_token(&ss,&sval);
2483                 switch (kind) {
2484                 case TK_EOS:
2485                         *code = LDAP_SCHERR_NORIGHTPAREN;
2486                         *errp = ss;
2487                         ldap_objectclass_free(oc);
2488                         return NULL;
2489                 case TK_RIGHTPAREN:
2490                         return oc;
2491                 case TK_BAREWORD:
2492                         if ( !strcasecmp(sval,"NAME") ) {
2493                                 LDAP_FREE(sval);
2494                                 if ( seen_name ) {
2495                                         *code = LDAP_SCHERR_DUPOPT;
2496                                         *errp = ss;
2497                                         ldap_objectclass_free(oc);
2498                                         return(NULL);
2499                                 }
2500                                 seen_name = 1;
2501                                 oc->oc_names = parse_qdescrs(&ss,code);
2502                                 if ( !oc->oc_names ) {
2503                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
2504                                                 *code = LDAP_SCHERR_BADNAME;
2505                                         *errp = ss;
2506                                         ldap_objectclass_free(oc);
2507                                         return NULL;
2508                                 }
2509                         } else if ( !strcasecmp(sval,"DESC") ) {
2510                                 LDAP_FREE(sval);
2511                                 if ( seen_desc ) {
2512                                         *code = LDAP_SCHERR_DUPOPT;
2513                                         *errp = ss;
2514                                         ldap_objectclass_free(oc);
2515                                         return(NULL);
2516                                 }
2517                                 seen_desc = 1;
2518                                 parse_whsp(&ss);
2519                                 kind = get_token(&ss,&sval);
2520                                 if ( kind != TK_QDSTRING ) {
2521                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2522                                         *errp = ss;
2523                                         LDAP_FREE(sval);
2524                                         ldap_objectclass_free(oc);
2525                                         return NULL;
2526                                 }
2527                                 oc->oc_desc = sval;
2528                                 parse_whsp(&ss);
2529                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
2530                                 LDAP_FREE(sval);
2531                                 if ( seen_obsolete ) {
2532                                         *code = LDAP_SCHERR_DUPOPT;
2533                                         *errp = ss;
2534                                         ldap_objectclass_free(oc);
2535                                         return(NULL);
2536                                 }
2537                                 seen_obsolete = 1;
2538                                 oc->oc_obsolete = LDAP_SCHEMA_YES;
2539                                 parse_whsp(&ss);
2540                         } else if ( !strcasecmp(sval,"SUP") ) {
2541                                 LDAP_FREE(sval);
2542                                 if ( seen_sup ) {
2543                                         *code = LDAP_SCHERR_DUPOPT;
2544                                         *errp = ss;
2545                                         ldap_objectclass_free(oc);
2546                                         return(NULL);
2547                                 }
2548                                 seen_sup = 1;
2549                                 oc->oc_sup_oids = parse_oids(&ss,
2550                                                              code,
2551                                                              flags);
2552                                 if ( !oc->oc_sup_oids && *code != LDAP_SUCCESS ) {
2553                                         *errp = ss;
2554                                         ldap_objectclass_free(oc);
2555                                         return NULL;
2556                                 }
2557                                 *code = 0;
2558                         } else if ( !strcasecmp(sval,"ABSTRACT") ) {
2559                                 LDAP_FREE(sval);
2560                                 if ( seen_kind ) {
2561                                         *code = LDAP_SCHERR_DUPOPT;
2562                                         *errp = ss;
2563                                         ldap_objectclass_free(oc);
2564                                         return(NULL);
2565                                 }
2566                                 seen_kind = 1;
2567                                 oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
2568                                 parse_whsp(&ss);
2569                         } else if ( !strcasecmp(sval,"STRUCTURAL") ) {
2570                                 LDAP_FREE(sval);
2571                                 if ( seen_kind ) {
2572                                         *code = LDAP_SCHERR_DUPOPT;
2573                                         *errp = ss;
2574                                         ldap_objectclass_free(oc);
2575                                         return(NULL);
2576                                 }
2577                                 seen_kind = 1;
2578                                 oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
2579                                 parse_whsp(&ss);
2580                         } else if ( !strcasecmp(sval,"AUXILIARY") ) {
2581                                 LDAP_FREE(sval);
2582                                 if ( seen_kind ) {
2583                                         *code = LDAP_SCHERR_DUPOPT;
2584                                         *errp = ss;
2585                                         ldap_objectclass_free(oc);
2586                                         return(NULL);
2587                                 }
2588                                 seen_kind = 1;
2589                                 oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
2590                                 parse_whsp(&ss);
2591                         } else if ( !strcasecmp(sval,"MUST") ) {
2592                                 LDAP_FREE(sval);
2593                                 if ( seen_must ) {
2594                                         *code = LDAP_SCHERR_DUPOPT;
2595                                         *errp = ss;
2596                                         ldap_objectclass_free(oc);
2597                                         return(NULL);
2598                                 }
2599                                 seen_must = 1;
2600                                 oc->oc_at_oids_must = parse_oids(&ss,code,0);
2601                                 if ( !oc->oc_at_oids_must && *code != LDAP_SUCCESS ) {
2602                                         *errp = ss;
2603                                         ldap_objectclass_free(oc);
2604                                         return NULL;
2605                                 }
2606                                 *code = 0;
2607                                 parse_whsp(&ss);
2608                         } else if ( !strcasecmp(sval,"MAY") ) {
2609                                 LDAP_FREE(sval);
2610                                 if ( seen_may ) {
2611                                         *code = LDAP_SCHERR_DUPOPT;
2612                                         *errp = ss;
2613                                         ldap_objectclass_free(oc);
2614                                         return(NULL);
2615                                 }
2616                                 seen_may = 1;
2617                                 oc->oc_at_oids_may = parse_oids(&ss,code,0);
2618                                 if ( !oc->oc_at_oids_may && *code != LDAP_SUCCESS ) {
2619                                         *errp = ss;
2620                                         ldap_objectclass_free(oc);
2621                                         return NULL;
2622                                 }
2623                                 *code = 0;
2624                                 parse_whsp(&ss);
2625                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
2626                                 /* Should be parse_qdstrings */
2627                                 ext_vals = parse_qdescrs(&ss, code);
2628                                 *code = 0;
2629                                 if ( !ext_vals ) {
2630                                         *errp = ss;
2631                                         ldap_objectclass_free(oc);
2632                                         return NULL;
2633                                 }
2634                                 if ( add_extension(&oc->oc_extensions,
2635                                                     sval, ext_vals) ) {
2636                                         *code = LDAP_SCHERR_OUTOFMEM;
2637                                         *errp = ss;
2638                                         LDAP_FREE(sval);
2639                                         ldap_objectclass_free(oc);
2640                                         return NULL;
2641                                 }
2642                         } else {
2643                                 *code = LDAP_SCHERR_UNEXPTOKEN;
2644                                 *errp = ss;
2645                                 LDAP_FREE(sval);
2646                                 ldap_objectclass_free(oc);
2647                                 return NULL;
2648                         }
2649                         break;
2650                 default:
2651                         *code = LDAP_SCHERR_UNEXPTOKEN;
2652                         *errp = ss;
2653                         LDAP_FREE(sval);
2654                         ldap_objectclass_free(oc);
2655                         return NULL;
2656                 }
2657         }
2658 }
2659
2660 void
2661 ldap_contentrule_free(LDAPContentRule * cr)
2662 {
2663         LDAP_FREE(cr->cr_oid);
2664         if (cr->cr_names) LDAP_VFREE(cr->cr_names);
2665         if (cr->cr_desc) LDAP_FREE(cr->cr_desc);
2666         if (cr->cr_oc_oids_aux) LDAP_VFREE(cr->cr_oc_oids_aux);
2667         if (cr->cr_at_oids_must) LDAP_VFREE(cr->cr_at_oids_must);
2668         if (cr->cr_at_oids_may) LDAP_VFREE(cr->cr_at_oids_may);
2669         if (cr->cr_at_oids_not) LDAP_VFREE(cr->cr_at_oids_not);
2670         free_extensions(cr->cr_extensions);
2671         LDAP_FREE(cr);
2672 }
2673
2674 LDAPContentRule *
2675 ldap_str2contentrule( LDAP_CONST char * s,
2676         int * code,
2677         LDAP_CONST char ** errp,
2678         LDAP_CONST unsigned flags )
2679 {
2680         tk_t kind;
2681         const char * ss = s;
2682         char * sval;
2683         int seen_name = 0;
2684         int seen_desc = 0;
2685         int seen_obsolete = 0;
2686         int seen_aux = 0;
2687         int seen_must = 0;
2688         int seen_may = 0;
2689         int seen_not = 0;
2690         LDAPContentRule * cr;
2691         char ** ext_vals;
2692         const char * savepos;
2693
2694         if ( !s ) {
2695                 *code = LDAP_SCHERR_EMPTY;
2696                 *errp = "";
2697                 return NULL;
2698         }
2699
2700         *errp = s;
2701         cr = LDAP_CALLOC(1,sizeof(LDAPContentRule));
2702
2703         if ( !cr ) {
2704                 *code = LDAP_SCHERR_OUTOFMEM;
2705                 return NULL;
2706         }
2707
2708         kind = get_token(&ss,&sval);
2709         if ( kind != TK_LEFTPAREN ) {
2710                 *code = LDAP_SCHERR_NOLEFTPAREN;
2711                 LDAP_FREE(sval);
2712                 ldap_contentrule_free(cr);
2713                 return NULL;
2714         }
2715
2716         /*
2717          * Definitions MUST begin with an OID in the numericoid format.
2718          */
2719         parse_whsp(&ss);
2720         savepos = ss;
2721         cr->cr_oid = ldap_int_parse_numericoid(&ss,code,0);
2722         if ( !cr->cr_oid ) {
2723                 if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) {
2724                         /* Backtracking */
2725                         ss = savepos;
2726                         kind = get_token(&ss,&sval);
2727                         if ( kind == TK_BAREWORD ) {
2728                                 if ( !strcasecmp(sval, "NAME") ||
2729                                      !strcasecmp(sval, "DESC") ||
2730                                      !strcasecmp(sval, "OBSOLETE") ||
2731                                      !strcasecmp(sval, "AUX") ||
2732                                      !strcasecmp(sval, "MUST") ||
2733                                      !strcasecmp(sval, "MAY") ||
2734                                      !strcasecmp(sval, "NOT") ||
2735                                      !strncasecmp(sval, "X-", 2) ) {
2736                                         /* Missing OID, backtrack */
2737                                         ss = savepos;
2738                                 } else if ( flags &
2739                                         LDAP_SCHEMA_ALLOW_OID_MACRO ) {
2740                                         /* Non-numerical OID, ignore */
2741                                         int len = ss-savepos;
2742                                         cr->cr_oid = LDAP_MALLOC(len+1);
2743                                         strncpy(cr->cr_oid, savepos, len);
2744                                         cr->cr_oid[len] = 0;
2745                                 }
2746                         }
2747                         LDAP_FREE(sval);
2748                 } else {
2749                         *errp = ss;
2750                         ldap_contentrule_free(cr);
2751                         return NULL;
2752                 }
2753         }
2754         parse_whsp(&ss);
2755
2756         /*
2757          * Beyond this point we will be liberal an accept the items
2758          * in any order.
2759          */
2760         while (1) {
2761                 kind = get_token(&ss,&sval);
2762                 switch (kind) {
2763                 case TK_EOS:
2764                         *code = LDAP_SCHERR_NORIGHTPAREN;
2765                         *errp = ss;
2766                         ldap_contentrule_free(cr);
2767                         return NULL;
2768                 case TK_RIGHTPAREN:
2769                         return cr;
2770                 case TK_BAREWORD:
2771                         if ( !strcasecmp(sval,"NAME") ) {
2772                                 LDAP_FREE(sval);
2773                                 if ( seen_name ) {
2774                                         *code = LDAP_SCHERR_DUPOPT;
2775                                         *errp = ss;
2776                                         ldap_contentrule_free(cr);
2777                                         return(NULL);
2778                                 }
2779                                 seen_name = 1;
2780                                 cr->cr_names = parse_qdescrs(&ss,code);
2781                                 if ( !cr->cr_names ) {
2782                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
2783                                                 *code = LDAP_SCHERR_BADNAME;
2784                                         *errp = ss;
2785                                         ldap_contentrule_free(cr);
2786                                         return NULL;
2787                                 }
2788                         } else if ( !strcasecmp(sval,"DESC") ) {
2789                                 LDAP_FREE(sval);
2790                                 if ( seen_desc ) {
2791                                         *code = LDAP_SCHERR_DUPOPT;
2792                                         *errp = ss;
2793                                         ldap_contentrule_free(cr);
2794                                         return(NULL);
2795                                 }
2796                                 seen_desc = 1;
2797                                 parse_whsp(&ss);
2798                                 kind = get_token(&ss,&sval);
2799                                 if ( kind != TK_QDSTRING ) {
2800                                         *code = LDAP_SCHERR_UNEXPTOKEN;
2801                                         *errp = ss;
2802                                         LDAP_FREE(sval);
2803                                         ldap_contentrule_free(cr);
2804                                         return NULL;
2805                                 }
2806                                 cr->cr_desc = sval;
2807                                 parse_whsp(&ss);
2808                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
2809                                 LDAP_FREE(sval);
2810                                 if ( seen_obsolete ) {
2811                                         *code = LDAP_SCHERR_DUPOPT;
2812                                         *errp = ss;
2813                                         ldap_contentrule_free(cr);
2814                                         return(NULL);
2815                                 }
2816                                 seen_obsolete = 1;
2817                                 cr->cr_obsolete = LDAP_SCHEMA_YES;
2818                                 parse_whsp(&ss);
2819                         } else if ( !strcasecmp(sval,"AUX") ) {
2820                                 LDAP_FREE(sval);
2821                                 if ( seen_aux ) {
2822                                         *code = LDAP_SCHERR_DUPOPT;
2823                                         *errp = ss;
2824                                         ldap_contentrule_free(cr);
2825                                         return(NULL);
2826                                 }
2827                                 seen_aux = 1;
2828                                 cr->cr_oc_oids_aux = parse_oids(&ss,code,0);
2829                                 if ( !cr->cr_oc_oids_aux ) {
2830                                         *errp = ss;
2831                                         ldap_contentrule_free(cr);
2832                                         return NULL;
2833                                 }
2834                                 parse_whsp(&ss);
2835                         } else if ( !strcasecmp(sval,"MUST") ) {
2836                                 LDAP_FREE(sval);
2837                                 if ( seen_must ) {
2838                                         *code = LDAP_SCHERR_DUPOPT;
2839                                         *errp = ss;
2840                                         ldap_contentrule_free(cr);
2841                                         return(NULL);
2842                                 }
2843                                 seen_must = 1;
2844                                 cr->cr_at_oids_must = parse_oids(&ss,code,0);
2845                                 if ( !cr->cr_at_oids_must && *code != LDAP_SUCCESS ) {
2846                                         *errp = ss;
2847                                         ldap_contentrule_free(cr);
2848                                         return NULL;
2849                                 }
2850                                 parse_whsp(&ss);
2851                         } else if ( !strcasecmp(sval,"MAY") ) {
2852                                 LDAP_FREE(sval);
2853                                 if ( seen_may ) {
2854                                         *code = LDAP_SCHERR_DUPOPT;
2855                                         *errp = ss;
2856                                         ldap_contentrule_free(cr);
2857                                         return(NULL);
2858                                 }
2859                                 seen_may = 1;
2860                                 cr->cr_at_oids_may = parse_oids(&ss,code,0);
2861                                 if ( !cr->cr_at_oids_may && *code != LDAP_SUCCESS ) {
2862                                         *errp = ss;
2863                                         ldap_contentrule_free(cr);
2864                                         return NULL;
2865                                 }
2866                                 parse_whsp(&ss);
2867                         } else if ( !strcasecmp(sval,"NOT") ) {
2868                                 LDAP_FREE(sval);
2869                                 if ( seen_not ) {
2870                                         *code = LDAP_SCHERR_DUPOPT;
2871                                         *errp = ss;
2872                                         ldap_contentrule_free(cr);
2873                                         return(NULL);
2874                                 }
2875                                 seen_not = 1;
2876                                 cr->cr_at_oids_not = parse_oids(&ss,code,0);
2877                                 if ( !cr->cr_at_oids_not && *code != LDAP_SUCCESS ) {
2878                                         *errp = ss;
2879                                         ldap_contentrule_free(cr);
2880                                         return NULL;
2881                                 }
2882                                 parse_whsp(&ss);
2883                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
2884                                 /* Should be parse_qdstrings */
2885                                 ext_vals = parse_qdescrs(&ss, code);
2886                                 if ( !ext_vals ) {
2887                                         *errp = ss;
2888                                         ldap_contentrule_free(cr);
2889                                         return NULL;
2890                                 }
2891                                 if ( add_extension(&cr->cr_extensions,
2892                                                     sval, ext_vals) ) {
2893                                         *code = LDAP_SCHERR_OUTOFMEM;
2894                                         *errp = ss;
2895                                         LDAP_FREE(sval);
2896                                         ldap_contentrule_free(cr);
2897                                         return NULL;
2898                                 }
2899                         } else {
2900                                 *code = LDAP_SCHERR_UNEXPTOKEN;
2901                                 *errp = ss;
2902                                 LDAP_FREE(sval);
2903                                 ldap_contentrule_free(cr);
2904                                 return NULL;
2905                         }
2906                         break;
2907                 default:
2908                         *code = LDAP_SCHERR_UNEXPTOKEN;
2909                         *errp = ss;
2910                         LDAP_FREE(sval);
2911                         ldap_contentrule_free(cr);
2912                         return NULL;
2913                 }
2914         }
2915 }
2916
2917 void
2918 ldap_structurerule_free(LDAPStructureRule * sr)
2919 {
2920         if (sr->sr_names) LDAP_VFREE(sr->sr_names);
2921         if (sr->sr_desc) LDAP_FREE(sr->sr_desc);
2922         if (sr->sr_nameform) LDAP_FREE(sr->sr_nameform);
2923         if (sr->sr_sup_ruleids) LDAP_FREE(sr->sr_sup_ruleids);
2924         free_extensions(sr->sr_extensions);
2925         LDAP_FREE(sr);
2926 }
2927
2928 LDAPStructureRule *
2929 ldap_str2structurerule( LDAP_CONST char * s,
2930         int * code,
2931         LDAP_CONST char ** errp,
2932         LDAP_CONST unsigned flags )
2933 {
2934         tk_t kind;
2935         int ret;
2936         const char * ss = s;
2937         char * sval;
2938         int seen_name = 0;
2939         int seen_desc = 0;
2940         int seen_obsolete = 0;
2941         int seen_nameform = 0;
2942         LDAPStructureRule * sr;
2943         char ** ext_vals;
2944         const char * savepos;
2945
2946         if ( !s ) {
2947                 *code = LDAP_SCHERR_EMPTY;
2948                 *errp = "";
2949                 return NULL;
2950         }
2951
2952         *errp = s;
2953         sr = LDAP_CALLOC(1,sizeof(LDAPStructureRule));
2954
2955         if ( !sr ) {
2956                 *code = LDAP_SCHERR_OUTOFMEM;
2957                 return NULL;
2958         }
2959
2960         kind = get_token(&ss,&sval);
2961         if ( kind != TK_LEFTPAREN ) {
2962                 *code = LDAP_SCHERR_NOLEFTPAREN;
2963                 LDAP_FREE(sval);
2964                 ldap_structurerule_free(sr);
2965                 return NULL;
2966         }
2967
2968         /*
2969          * Definitions MUST begin with a ruleid.
2970          */
2971         parse_whsp(&ss);
2972         savepos = ss;
2973         ret = ldap_int_parse_ruleid(&ss,code,0,&sr->sr_ruleid);
2974         if ( ret ) {
2975                 *errp = ss;
2976                 ldap_structurerule_free(sr);
2977                 return NULL;
2978         }
2979         parse_whsp(&ss);
2980
2981         /*
2982          * Beyond this point we will be liberal an accept the items
2983          * in any order.
2984          */
2985         while (1) {
2986                 kind = get_token(&ss,&sval);
2987                 switch (kind) {
2988                 case TK_EOS:
2989                         *code = LDAP_SCHERR_NORIGHTPAREN;
2990                         *errp = ss;
2991                         ldap_structurerule_free(sr);
2992                         return NULL;
2993                 case TK_RIGHTPAREN:
2994                         if( !seen_nameform ) {
2995                                 *code = LDAP_SCHERR_MISSING;
2996                                 ldap_structurerule_free(sr);
2997                                 return NULL;
2998                         }
2999                         return sr;
3000                 case TK_BAREWORD:
3001                         if ( !strcasecmp(sval,"NAME") ) {
3002                                 LDAP_FREE(sval);
3003                                 if ( seen_name ) {
3004                                         *code = LDAP_SCHERR_DUPOPT;
3005                                         *errp = ss;
3006                                         ldap_structurerule_free(sr);
3007                                         return(NULL);
3008                                 }
3009                                 seen_name = 1;
3010                                 sr->sr_names = parse_qdescrs(&ss,code);
3011                                 if ( !sr->sr_names ) {
3012                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
3013                                                 *code = LDAP_SCHERR_BADNAME;
3014                                         *errp = ss;
3015                                         ldap_structurerule_free(sr);
3016                                         return NULL;
3017                                 }
3018                         } else if ( !strcasecmp(sval,"DESC") ) {
3019                                 LDAP_FREE(sval);
3020                                 if ( seen_desc ) {
3021                                         *code = LDAP_SCHERR_DUPOPT;
3022                                         *errp = ss;
3023                                         ldap_structurerule_free(sr);
3024                                         return(NULL);
3025                                 }
3026                                 seen_desc = 1;
3027                                 parse_whsp(&ss);
3028                                 kind = get_token(&ss,&sval);
3029                                 if ( kind != TK_QDSTRING ) {
3030                                         *code = LDAP_SCHERR_UNEXPTOKEN;
3031                                         *errp = ss;
3032                                         LDAP_FREE(sval);
3033                                         ldap_structurerule_free(sr);
3034                                         return NULL;
3035                                 }
3036                                 sr->sr_desc = sval;
3037                                 parse_whsp(&ss);
3038                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
3039                                 LDAP_FREE(sval);
3040                                 if ( seen_obsolete ) {
3041                                         *code = LDAP_SCHERR_DUPOPT;
3042                                         *errp = ss;
3043                                         ldap_structurerule_free(sr);
3044                                         return(NULL);
3045                                 }
3046                                 seen_obsolete = 1;
3047                                 sr->sr_obsolete = LDAP_SCHEMA_YES;
3048                                 parse_whsp(&ss);
3049                         } else if ( !strcasecmp(sval,"FORM") ) {
3050                                 LDAP_FREE(sval);
3051                                 if ( seen_nameform ) {
3052                                         *code = LDAP_SCHERR_DUPOPT;
3053                                         *errp = ss;
3054                                         ldap_structurerule_free(sr);
3055                                         return(NULL);
3056                                 }
3057                                 seen_nameform = 1;
3058                                 sr->sr_nameform = parse_woid(&ss,code);
3059                                 if ( !sr->sr_nameform ) {
3060                                         *errp = ss;
3061                                         ldap_structurerule_free(sr);
3062                                         return NULL;
3063                                 }
3064                                 parse_whsp(&ss);
3065                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
3066                                 /* Should be parse_qdstrings */
3067                                 ext_vals = parse_qdescrs(&ss, code);
3068                                 if ( !ext_vals ) {
3069                                         *errp = ss;
3070                                         ldap_structurerule_free(sr);
3071                                         return NULL;
3072                                 }
3073                                 if ( add_extension(&sr->sr_extensions,
3074                                                     sval, ext_vals) ) {
3075                                         *code = LDAP_SCHERR_OUTOFMEM;
3076                                         *errp = ss;
3077                                         LDAP_FREE(sval);
3078                                         ldap_structurerule_free(sr);
3079                                         return NULL;
3080                                 }
3081                         } else {
3082                                 *code = LDAP_SCHERR_UNEXPTOKEN;
3083                                 *errp = ss;
3084                                 LDAP_FREE(sval);
3085                                 ldap_structurerule_free(sr);
3086                                 return NULL;
3087                         }
3088                         break;
3089                 default:
3090                         *code = LDAP_SCHERR_UNEXPTOKEN;
3091                         *errp = ss;
3092                         LDAP_FREE(sval);
3093                         ldap_structurerule_free(sr);
3094                         return NULL;
3095                 }
3096         }
3097 }
3098
3099 void
3100 ldap_nameform_free(LDAPNameForm * nf)
3101 {
3102         LDAP_FREE(nf->nf_oid);
3103         if (nf->nf_names) LDAP_VFREE(nf->nf_names);
3104         if (nf->nf_desc) LDAP_FREE(nf->nf_desc);
3105         if (nf->nf_objectclass) LDAP_FREE(nf->nf_objectclass);
3106         if (nf->nf_at_oids_must) LDAP_VFREE(nf->nf_at_oids_must);
3107         if (nf->nf_at_oids_may) LDAP_VFREE(nf->nf_at_oids_may);
3108         free_extensions(nf->nf_extensions);
3109         LDAP_FREE(nf);
3110 }
3111
3112 LDAPNameForm *
3113 ldap_str2nameform( LDAP_CONST char * s,
3114         int * code,
3115         LDAP_CONST char ** errp,
3116         LDAP_CONST unsigned flags )
3117 {
3118         tk_t kind;
3119         const char * ss = s;
3120         char * sval;
3121         int seen_name = 0;
3122         int seen_desc = 0;
3123         int seen_obsolete = 0;
3124         int seen_class = 0;
3125         int seen_must = 0;
3126         int seen_may = 0;
3127         LDAPNameForm * nf;
3128         char ** ext_vals;
3129         const char * savepos;
3130
3131         if ( !s ) {
3132                 *code = LDAP_SCHERR_EMPTY;
3133                 *errp = "";
3134                 return NULL;
3135         }
3136
3137         *errp = s;
3138         nf = LDAP_CALLOC(1,sizeof(LDAPNameForm));
3139
3140         if ( !nf ) {
3141                 *code = LDAP_SCHERR_OUTOFMEM;
3142                 return NULL;
3143         }
3144
3145         kind = get_token(&ss,&sval);
3146         if ( kind != TK_LEFTPAREN ) {
3147                 *code = LDAP_SCHERR_NOLEFTPAREN;
3148                 LDAP_FREE(sval);
3149                 ldap_nameform_free(nf);
3150                 return NULL;
3151         }
3152
3153         /*
3154          * Definitions MUST begin with an OID in the numericoid format.
3155          * However, this routine is used by clients to parse the response
3156          * from servers and very well known servers will provide an OID
3157          * in the wrong format or even no OID at all.  We do our best to
3158          * extract info from those servers.
3159          */
3160         parse_whsp(&ss);
3161         savepos = ss;
3162         nf->nf_oid = ldap_int_parse_numericoid(&ss,code,0);
3163         if ( !nf->nf_oid ) {
3164                 *errp = ss;
3165                 ldap_nameform_free(nf);
3166                 return NULL;
3167         }
3168         parse_whsp(&ss);
3169
3170         /*
3171          * Beyond this point we will be liberal an accept the items
3172          * in any order.
3173          */
3174         while (1) {
3175                 kind = get_token(&ss,&sval);
3176                 switch (kind) {
3177                 case TK_EOS:
3178                         *code = LDAP_SCHERR_NORIGHTPAREN;
3179                         *errp = ss;
3180                         ldap_nameform_free(nf);
3181                         return NULL;
3182                 case TK_RIGHTPAREN:
3183                         if( !seen_class || !seen_must ) {
3184                                 *code = LDAP_SCHERR_MISSING;
3185                                 ldap_nameform_free(nf);
3186                                 return NULL;
3187                         }
3188                         return nf;
3189                 case TK_BAREWORD:
3190                         if ( !strcasecmp(sval,"NAME") ) {
3191                                 LDAP_FREE(sval);
3192                                 if ( seen_name ) {
3193                                         *code = LDAP_SCHERR_DUPOPT;
3194                                         *errp = ss;
3195                                         ldap_nameform_free(nf);
3196                                         return(NULL);
3197                                 }
3198                                 seen_name = 1;
3199                                 nf->nf_names = parse_qdescrs(&ss,code);
3200                                 if ( !nf->nf_names ) {
3201                                         if ( *code != LDAP_SCHERR_OUTOFMEM )
3202                                                 *code = LDAP_SCHERR_BADNAME;
3203                                         *errp = ss;
3204                                         ldap_nameform_free(nf);
3205                                         return NULL;
3206                                 }
3207                         } else if ( !strcasecmp(sval,"DESC") ) {
3208                                 LDAP_FREE(sval);
3209                                 if ( seen_desc ) {
3210                                         *code = LDAP_SCHERR_DUPOPT;
3211                                         *errp = ss;
3212                                         ldap_nameform_free(nf);
3213                                         return(NULL);
3214                                 }
3215                                 seen_desc = 1;
3216                                 parse_whsp(&ss);
3217                                 kind = get_token(&ss,&sval);
3218                                 if ( kind != TK_QDSTRING ) {
3219                                         *code = LDAP_SCHERR_UNEXPTOKEN;
3220                                         *errp = ss;
3221                                         LDAP_FREE(sval);
3222                                         ldap_nameform_free(nf);
3223                                         return NULL;
3224                                 }
3225                                 nf->nf_desc = sval;
3226                                 parse_whsp(&ss);
3227                         } else if ( !strcasecmp(sval,"OBSOLETE") ) {
3228                                 LDAP_FREE(sval);
3229                                 if ( seen_obsolete ) {
3230                                         *code = LDAP_SCHERR_DUPOPT;
3231                                         *errp = ss;
3232                                         ldap_nameform_free(nf);
3233                                         return(NULL);
3234                                 }
3235                                 seen_obsolete = 1;
3236                                 nf->nf_obsolete = LDAP_SCHEMA_YES;
3237                                 parse_whsp(&ss);
3238                         } else if ( !strcasecmp(sval,"OC") ) {
3239                                 LDAP_FREE(sval);
3240                                 if ( seen_class ) {
3241                                         *code = LDAP_SCHERR_DUPOPT;
3242                                         *errp = ss;
3243                                         ldap_nameform_free(nf);
3244                                         return(NULL);
3245                                 }
3246                                 seen_class = 1;
3247                                 nf->nf_objectclass = parse_woid(&ss,code);
3248                                 if ( !nf->nf_objectclass ) {
3249                                         *errp = ss;
3250                                         ldap_nameform_free(nf);
3251                                         return NULL;
3252                                 }
3253                         } else if ( !strcasecmp(sval,"MUST") ) {
3254                                 LDAP_FREE(sval);
3255                                 if ( seen_must ) {
3256                                         *code = LDAP_SCHERR_DUPOPT;
3257                                         *errp = ss;
3258                                         ldap_nameform_free(nf);
3259                                         return(NULL);
3260                                 }
3261                                 seen_must = 1;
3262                                 nf->nf_at_oids_must = parse_oids(&ss,code,0);
3263                                 if ( !nf->nf_at_oids_must && *code != LDAP_SUCCESS ) {
3264                                         *errp = ss;
3265                                         ldap_nameform_free(nf);
3266                                         return NULL;
3267                                 }
3268                                 parse_whsp(&ss);
3269                         } else if ( !strcasecmp(sval,"MAY") ) {
3270                                 LDAP_FREE(sval);
3271                                 if ( seen_may ) {
3272                                         *code = LDAP_SCHERR_DUPOPT;
3273                                         *errp = ss;
3274                                         ldap_nameform_free(nf);
3275                                         return(NULL);
3276                                 }
3277                                 seen_may = 1;
3278                                 nf->nf_at_oids_may = parse_oids(&ss,code,0);
3279                                 if ( !nf->nf_at_oids_may && *code != LDAP_SUCCESS ) {
3280                                         *errp = ss;
3281                                         ldap_nameform_free(nf);
3282                                         return NULL;
3283                                 }
3284                                 parse_whsp(&ss);
3285                         } else if ( sval[0] == 'X' && sval[1] == '-' ) {
3286                                 /* Should be parse_qdstrings */
3287                                 ext_vals = parse_qdescrs(&ss, code);
3288                                 if ( !ext_vals ) {
3289                                         *errp = ss;
3290                                         ldap_nameform_free(nf);
3291                                         return NULL;
3292                                 }
3293                                 if ( add_extension(&nf->nf_extensions,
3294                                                     sval, ext_vals) ) {
3295                                         *code = LDAP_SCHERR_OUTOFMEM;
3296                                         *errp = ss;
3297                                         LDAP_FREE(sval);
3298                                         ldap_nameform_free(nf);
3299                                         return NULL;
3300                                 }
3301                         } else {
3302                                 *code = LDAP_SCHERR_UNEXPTOKEN;
3303                                 *errp = ss;
3304                                 LDAP_FREE(sval);
3305                                 ldap_nameform_free(nf);
3306                                 return NULL;
3307                         }
3308                         break;
3309                 default:
3310                         *code = LDAP_SCHERR_UNEXPTOKEN;
3311                         *errp = ss;
3312                         LDAP_FREE(sval);
3313                         ldap_nameform_free(nf);
3314                         return NULL;
3315                 }
3316         }
3317 }
3318
3319 static char *const err2text[] = {
3320         N_("Success"),
3321         N_("Out of memory"),
3322         N_("Unexpected token"),
3323         N_("Missing opening parenthesis"),
3324         N_("Missing closing parenthesis"),
3325         N_("Expecting digit"),
3326         N_("Expecting a name"),
3327         N_("Bad description"),
3328         N_("Bad superiors"),
3329         N_("Duplicate option"),
3330         N_("Unexpected end of data"),
3331         N_("Missing required field"),
3332         N_("Out of order field")
3333 };
3334
3335 char *
3336 ldap_scherr2str(int code)
3337 {
3338         if ( code < 0 || code >= (int)(sizeof(err2text)/sizeof(char *)) ) {
3339                 return _("Unknown error");
3340         } else {
3341                 return _(err2text[code]);
3342         }
3343 }