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