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