Merge branch 'v4-0-test' of git://git.samba.org/samba into 4-0-local
[kai/samba.git] / testprogs / ejs / minschema.js
1 #!/bin/sh
2 exec smbscript "$0" ${1+"$@"}
3 /*
4   work out the minimal schema for a set of objectclasses 
5 */
6
7 libinclude("base.js");
8
9 var ldb = ldb_init();
10
11 var options = GetOptions(ARGV, 
12                          "POPT_AUTOHELP",
13                          "POPT_COMMON_SAMBA",
14                          "POPT_COMMON_CREDENTIALS",
15                          "verbose",
16                          "classes",
17                          "attributes",
18                          "subschema",
19                          "subschema-auto");
20 if (options == undefined) {
21    println("Failed to parse options");
22    return -1;
23 }
24 verbose = options["verbose"];
25 dump_all = "yes";
26 dump_classes = options["classes"];
27 dump_attributes = options["attributes"];
28 dump_subschema = options["subschema"];
29 dump_subschema_auto = options["subschema-auto"];
30
31 if (dump_classes != undefined) {
32         dump_all = undefined;
33 }
34 if (dump_attributes != undefined) {
35         dump_all = undefined;
36 }
37 if (dump_subschema != undefined) {
38         dump_all = undefined;
39 }
40 if (dump_subschema_auto != undefined) {
41         dump_all = undefined;
42         dump_subschema = "yes";
43 }
44 if (dump_all != undefined) {
45         dump_classes = "yes";
46         dump_attributes = "yes";
47         dump_subschema = "yes";
48         dump_subschema_auto = "yes";
49 }
50
51 if (options.ARGV.length != 2) {
52    println("Usage: minschema.js <URL> <classfile>");
53    return -1;
54 }
55
56 var url = options.ARGV[0];
57 var classfile = options.ARGV[1];
58
59 /* use command line creds if available */
60 ldb.credentials = options.get_credentials();
61
62 var ok = ldb.connect(url);
63 assert(ok);
64
65 objectclasses = new Object();
66 attributes = new Object();
67 rootDse = new Object();
68
69 objectclasses_expanded = new Object();
70
71 /* the attributes we need for objectclasses */
72 class_attrs = new Array("objectClass",
73                         "subClassOf",
74                         "governsID",
75                         "possSuperiors",
76                         "possibleInferiors",
77                         "mayContain",
78                         "mustContain",
79                         "auxiliaryClass",
80                         "rDNAttID",
81                         "showInAdvancedViewOnly",
82                         "adminDisplayName",
83                         "adminDescription",
84                         "objectClassCategory",
85                         "lDAPDisplayName",
86                         "schemaIDGUID",
87                         "systemOnly",
88                         "systemPossSuperiors",
89                         "systemMayContain",
90                         "systemMustContain",
91                         "systemAuxiliaryClass",
92                         "defaultSecurityDescriptor",
93                         "systemFlags",
94                         "defaultHidingValue",
95                         "objectCategory",
96                         "defaultObjectCategory",
97
98                         /* this attributes are not used by w2k3 */
99                         "schemaFlagsEx",
100                         "msDs-IntId",
101                         "msDs-Schema-Extensions",
102                         "classDisplayName",
103                         "isDefunct");
104
105
106 attrib_attrs = new Array("objectClass",
107                          "attributeID",
108                          "attributeSyntax",
109                          "isSingleValued",
110                          "rangeLower",
111                          "rangeUpper",
112                          "mAPIID",
113                          "linkID",
114                          "showInAdvancedViewOnly",
115                          "adminDisplayName",
116                          "oMObjectClass",
117                          "adminDescription",
118                          "oMSyntax",
119                          "searchFlags",
120                          "extendedCharsAllowed",
121                          "lDAPDisplayName",
122                          "schemaIDGUID",
123                          "attributeSecurityGUID",
124                          "systemOnly",
125                          "systemFlags",
126                          "isMemberOfPartialAttributeSet",
127                          "objectCategory",
128
129                          /* this attributes are not used by w2k3 */
130                          "schemaFlagsEx",
131                          "msDs-IntId",
132                          "msDs-Schema-Extensions",
133                          "classDisplayName",
134                          "isEphemeral",
135                          "isDefunct");
136
137 /*
138   notes:
139
140   objectClassCategory 
141       1: structural
142       2: abstract
143       3: auxiliary
144 */
145
146
147 /*
148   print only if verbose is set
149 */
150 function dprintf() {
151         if (verbose != undefined) {
152                 print(vsprintf(arguments));
153         }
154 }
155
156 function get_object_cn(ldb, name) {
157         var attrs = new Array("cn");
158
159         var res = ldb.search(sprintf("(ldapDisplayName=%s)", name), rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
160         assert(res != undefined);
161         assert(res.msgs.length == 1);
162
163         var cn = res.msgs[0]["cn"];
164         assert(cn != undefined);
165         if (typeof(cn) == "string") {
166                 return cn;
167         }
168         return cn[0];
169 }
170 /*
171   create an objectclass object
172 */
173 function obj_objectClass(ldb, name) {
174         var o = new Object();
175         o.name = name;
176         o.cn = get_object_cn(ldb, name);
177         return o;
178 }
179
180 /*
181   create an attribute object
182 */
183 function obj_attribute(ldb, name) {
184         var o = new Object();
185         o.name = name;
186         o.cn = get_object_cn(ldb, name);
187         return o;
188 }
189
190
191 syntaxmap = new Object();
192
193 syntaxmap['2.5.5.1']  = '1.3.6.1.4.1.1466.115.121.1.12';
194 syntaxmap['2.5.5.2']  = '1.3.6.1.4.1.1466.115.121.1.38';
195 syntaxmap['2.5.5.3']  = '1.2.840.113556.1.4.1362';
196 syntaxmap['2.5.5.4']  = '1.2.840.113556.1.4.905';
197 syntaxmap['2.5.5.5']  = '1.3.6.1.4.1.1466.115.121.1.26';
198 syntaxmap['2.5.5.6']  = '1.3.6.1.4.1.1466.115.121.1.36';
199 syntaxmap['2.5.5.7']  = '1.2.840.113556.1.4.903';
200 syntaxmap['2.5.5.8']  = '1.3.6.1.4.1.1466.115.121.1.7';
201 syntaxmap['2.5.5.9']  = '1.3.6.1.4.1.1466.115.121.1.27';
202 syntaxmap['2.5.5.10'] = '1.3.6.1.4.1.1466.115.121.1.40';
203 syntaxmap['2.5.5.11'] = '1.3.6.1.4.1.1466.115.121.1.24';
204 syntaxmap['2.5.5.12'] = '1.3.6.1.4.1.1466.115.121.1.15';
205 syntaxmap['2.5.5.13'] = '1.3.6.1.4.1.1466.115.121.1.43';
206 syntaxmap['2.5.5.14'] = '1.2.840.113556.1.4.904';
207 syntaxmap['2.5.5.15'] = '1.2.840.113556.1.4.907';
208 syntaxmap['2.5.5.16'] = '1.2.840.113556.1.4.906';
209 syntaxmap['2.5.5.17'] = '1.3.6.1.4.1.1466.115.121.1.40';
210
211 /*
212   map some attribute syntaxes from some apparently MS specific
213   syntaxes to the standard syntaxes
214 */
215 function map_attribute_syntax(s) {
216         if (syntaxmap[s] != undefined) {
217                 return syntaxmap[s];
218         }
219         return s;
220 }
221
222
223 /*
224   fix a string DN to use ${SCHEMADN}
225 */
226 function fix_dn(dn) {
227         var s = strstr(dn, rootDse.schemaNamingContext);
228         if (s == NULL) {
229                 return dn;
230         }
231         return substr(dn, 0, strlen(dn) - strlen(s)) + "${SCHEMADN}";
232 }
233
234 /*
235   dump an object as ldif
236 */
237 function write_ldif_one(o, attrs) {
238         var i;
239         printf("dn: CN=%s,${SCHEMADN}\n", o.cn);
240         for (i=0;i<attrs.length;i++) {
241                 var a = attrs[i];
242                 if (o[a] == undefined) {
243                         continue;
244                 }
245                 /* special case for oMObjectClass, which is a binary object */
246                 if (a == "oMObjectClass") {
247                         printf("%s:: %s\n", a, o[a]);
248                         continue;
249                 }
250                 var v = o[a];
251                 if (typeof(v) == "string") {
252                         v = new Array(v);
253                 }
254                 var j;
255                 for (j=0;j<v.length;j++) {
256                         printf("%s: %s\n", a, fix_dn(v[j]));
257                 }
258         }
259         printf("\n");
260 }
261
262 /*
263   dump an array of objects as ldif
264 */
265 function write_ldif(o, attrs) {
266         var i;
267         for (i in o) {
268                 write_ldif_one(o[i], attrs);
269         }
270 }
271
272
273 /*
274   create a testDN based an an example DN
275   the idea is to ensure we obey any structural rules
276 */
277 function create_testdn(exampleDN) {
278         var a = split(",", exampleDN);
279         a[0] = "CN=TestDN";
280         return join(",", a);
281 }
282
283 /*
284   find the properties of an objectclass
285  */
286 function find_objectclass_properties(ldb, o) {
287         var res = ldb.search(
288                 sprintf("(ldapDisplayName=%s)", o.name),
289                 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, class_attrs);
290         assert(res != undefined);
291         assert(res.msgs.length == 1);
292         var msg = res.msgs[0];
293         var a;
294         for (a in msg) {
295                 o[a] = msg[a];
296         }
297 }
298
299 /*
300   find the properties of an attribute
301  */
302 function find_attribute_properties(ldb, o) {
303         var res = ldb.search(
304                 sprintf("(ldapDisplayName=%s)", o.name),
305                 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrib_attrs);
306         assert(res != undefined);
307         assert(res.msgs.length == 1);
308         var msg = res.msgs[0];
309         var a;
310         for (a in msg) {
311                 /* special case for oMObjectClass, which is a binary object */
312                 if (a == "oMObjectClass") {
313                         o[a] = ldb.encode(msg[a]);
314                         continue;
315                 }
316                 o[a] = msg[a];
317         }
318 }
319
320 /*
321   find the auto-created properties of an objectclass. Only works for classes
322   that can be created using just a DN and the objectclass
323  */
324 function find_objectclass_auto(ldb, o) {
325         if (o["exampleDN"] == undefined) {
326                 return;
327         }
328         var testdn = create_testdn(o.exampleDN);
329         var ok;
330
331         dprintf("testdn is '%s'\n", testdn);
332
333         var ldif = "dn: " + testdn;
334         ldif = ldif + "\nobjectClass: " + o.name;
335         ok = ldb.add(ldif);
336         if (ok.error != 0) {
337                 dprintf("error adding %s: %s\n", o.name, ok.errstr);
338                 dprintf("%s\n", ldif);
339                 return;
340         }
341
342         var res = ldb.search("", testdn, ldb.SCOPE_BASE);
343         ok = ldb.del(testdn);
344         assert(ok.error == 0);
345
346         var a;
347         for (a in res.msgs[0]) {
348                 attributes[a].autocreate = true;
349         }
350 }
351
352
353 /*
354   look at auxiliary information from a class to intuit the existance of more
355   classes needed for a minimal schema
356 */
357 function expand_objectclass(ldb, o) {
358         var attrs = new Array("auxiliaryClass", "systemAuxiliaryClass",
359                               "possSuperiors", "systemPossSuperiors",
360                               "subClassOf");
361         var res = ldb.search(
362                 sprintf("(&(objectClass=classSchema)(ldapDisplayName=%s))", o.name),
363                 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
364         var a;
365         dprintf("Expanding class %s\n", o.name);
366         assert(res != undefined);
367         assert(res.msgs.length == 1);
368         var msg = res.msgs[0];
369         for (a=0;a<attrs.length;a++) {
370                 var aname = attrs[a];
371                 if (msg[aname] == undefined) {
372                         continue;
373                 }
374                 var list = msg[aname];
375                 if (typeof(list) == "string") {
376                         list = new Array(msg[aname]);
377                 }
378                 var i;
379                 for (i=0;i<list.length;i++) {
380                         var name = list[i];
381                         if (objectclasses[name] == undefined) {
382                                 dprintf("Found new objectclass '%s'\n", name);
383                                 objectclasses[name] = obj_objectClass(ldb, name);
384                         }
385                 }
386         }
387 }
388
389
390 /*
391   add the must and may attributes from an objectclass to the full list
392   of attributes
393 */
394 function add_objectclass_attributes(ldb, class) {
395         var attrs = new Array("mustContain", "systemMustContain", 
396                               "mayContain", "systemMayContain");
397         var i;
398         for (i=0;i<attrs.length;i++) {
399                 var aname = attrs[i];
400                 if (class[aname] == undefined) {
401                         continue;
402                 }
403                 var alist = class[aname];
404                 if (typeof(alist) == "string") {
405                         alist = new Array(alist);
406                 }
407                 var j;
408                 var len = alist.length;
409                 for (j=0;j<len;j++) {
410                         var a = alist[j];
411                         if (attributes[a] == undefined) {
412                                 attributes[a] = obj_attribute(ldb, a);
413                         }
414                 }
415         }
416 }
417
418
419 /*
420   process an individual record, working out what attributes it has
421 */
422 function walk_dn(ldb, dn) {
423         /* get a list of all possible attributes for this object */
424         var attrs = new Array("allowedAttributes");
425         var res = ldb.search("objectClass=*", dn, ldb.SCOPE_BASE, attrs);
426         if (res.error != 0) {
427                 dprintf("Unable to fetch allowedAttributes for '%s' - %s\n", 
428                        dn, res.errstr);
429                 return;
430         }
431         var allattrs = res.msgs[0].allowedAttributes;
432         res = ldb.search("objectClass=*", dn, ldb.SCOPE_BASE, allattrs);
433         if (res.error != 0) {
434                 dprintf("Unable to fetch all attributes for '%s' - %s\n", 
435                        dn, res.errstr);
436                 return;
437         }
438         var a;
439         var msg = res.msgs[0];
440         for (a in msg) {
441                 if (attributes[a] == undefined) {
442                         attributes[a] = obj_attribute(ldb, a);
443                 }
444         }
445 }
446
447 /*
448   walk a naming context, looking for all records
449 */
450 function walk_naming_context(ldb, namingContext) {
451         var attrs = new Array("objectClass");
452         var res = ldb.search("objectClass=*", namingContext, ldb.SCOPE_DEFAULT, attrs);
453         if (res.error != 0) {
454                 dprintf("Unable to fetch objectClasses for '%s' - %s\n", 
455                        namingContext, res.errstr);
456                 return;
457         }
458         var r;
459         for (r=0;r<res.msgs.length;r++) {
460                 var msg = res.msgs[r].objectClass;
461                 var c;
462                 for (c=0;c<msg.length;c++) {
463                         var objectClass = msg[c];
464                         if (objectclasses[objectClass] == undefined) {
465                                 objectclasses[objectClass] = obj_objectClass(ldb, objectClass);
466                                 objectclasses[objectClass].exampleDN = res.msgs[r].dn;
467                         }
468                 }
469                 walk_dn(ldb, res.msgs[r].dn);
470         }
471 }
472
473 /*
474   trim the may attributes for an objectClass
475 */
476 function trim_objectclass_attributes(ldb, class) {
477         var i,j,n;
478
479         /* trim possibleInferiors,
480          * include only the classes we extracted */
481         var possinf = class["possibleInferiors"];
482         if (possinf != undefined) {
483                 var newpossinf = new Array();
484                 if (typeof(possinf) == "string") {
485                         possinf = new Array(possinf);
486                 }
487                 n = 0;
488                 for (j = 0;j < possinf.length; j++) {
489                         var x = possinf[j];
490                         if (objectclasses[x] != undefined) {
491                                 newpossinf[n] = x;
492                                 n++;
493                         }
494                 }
495                 class["possibleInferiors"] = newpossinf;
496         }
497
498         /* trim systemMayContain,
499          * remove duplicates */
500         var sysmay = class["systemMayContain"];
501         if (sysmay != undefined) {
502                 var newsysmay = new Array();
503                 if (typeof(sysmay) == "string") {
504                         sysmay = new Array(sysmay);
505                 }
506                 for (j = 0;j < sysmay.length; j++) {
507                         var x = sysmay[j];
508                         var dup = false;
509                         if (newsysmay[0] == undefined) {
510                                 newsysmay[0] = x;
511                         } else {
512                                 for (n = 0; n < newsysmay.length; n++) {
513                                         if (newsysmay[n] == x) {
514                                                 dup = true;
515                                         }
516                                 }
517                                 if (dup == false) {
518                                         newsysmay[n] = x;
519                                 }
520                         }
521                 }
522                 class["systemMayContain"] = newsysmay;
523         }
524
525         /* trim mayContain,
526          * remove duplicates */
527         var may = class["mayContain"];
528         if (may != undefined) {
529                 var newmay = new Array();
530                 if (typeof(may) == "string") {
531                         may = new Array(may);
532                 }
533                 for (j = 0;j < may.length; j++) {
534                         var x = may[j];
535                         var dup = false;
536                         if (newmay[0] == undefined) {
537                                 newmay[0] = x;
538                         } else {
539                                 for (n = 0; n < newmay.length; n++) {
540                                         if (newmay[n] == x) {
541                                                 dup = true;
542                                         }
543                                 }
544                                 if (dup == false) {
545                                         newmay[n] = x;
546                                 }
547                         }
548                 }
549                 class["mayContain"] = newmay;
550         }
551 }
552
553 /*
554   load the basic attributes of an objectClass
555 */
556 function build_objectclass(ldb, name) {
557         var attrs = new Array("name");
558         var res = ldb.search(
559                 sprintf("(&(objectClass=classSchema)(ldapDisplayName=%s))", name),
560                 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
561         if (res.error != 0) {
562                 dprintf("unknown class '%s'\n", name);
563                 return undefined;
564         }
565         if (res.msgs.length == 0) {
566                 dprintf("unknown class '%s'\n", name);
567                 return undefined;
568         }
569         return obj_objectClass(ldb, name);
570 }
571
572 /*
573   append 2 lists
574 */
575 function list_append(a1, a2) {
576         var i;
577         if (a1 == undefined) {
578                 return a2;
579         }
580         if (a2 == undefined) {
581                 return a1;
582         }
583         for (i=0;i<a2.length;i++) {
584                 a1[a1.length] = a2[i];
585         }
586         return a1;
587 }
588
589 /*
590   form a coalesced attribute list
591 */
592 function attribute_list(class, attr1, attr2) {
593         var a1 = class[attr1];
594         var a2 = class[attr2];
595         if (typeof(a1) == "string") {
596                 a1 = new Array(a1);
597         }
598         if (typeof(a2) == "string") {
599                 a2 = new Array(a2);
600         }
601         return list_append(a1, a2);
602 }
603
604 /*
605   write out a list in aggregate form
606 */
607 function aggregate_list(name, list) {
608         if (list == undefined) {
609                 return;
610         }
611         var i;
612         printf("%s ( ", name);
613         for (i=0;i<list.length;i++) {
614                 printf("%s ", list[i]);
615                 if (i < (list.length - 1)) {
616                         printf("$ ");
617                 }
618         }
619         printf(") ");
620 }
621
622 /*
623   write the aggregate record for an objectclass
624 */
625 function write_aggregate_objectclass(class) {
626         printf("objectClasses: ( %s NAME '%s' ", class.governsID, class.name);
627         if (class['subClassOf'] != undefined) {
628                 printf("SUP %s ", class['subClassOf']);
629         }
630         if (class.objectClassCategory == 1) {
631                 printf("STRUCTURAL ");
632         } else if (class.objectClassCategory == 2) {
633                 printf("ABSTRACT ");
634         } else if (class.objectClassCategory == 3) {
635                 printf("AUXILIARY ");
636         }
637
638         var list;
639
640         list = attribute_list(class, "systemMustContain", "mustContain");
641         aggregate_list("MUST", list);
642
643         list = attribute_list(class, "systemMayContain", "mayContain");
644         aggregate_list("MAY", list);
645
646         printf(")\n");
647 }
648
649
650 /*
651   write the aggregate record for an ditcontentrule
652 */
653 function write_aggregate_ditcontentrule(class) {
654         var list = attribute_list(class, "auxiliaryClass", "systemAuxiliaryClass");
655         var i;
656         if (list == undefined) {
657                 return;
658         }
659
660         printf("dITContentRules: ( %s NAME '%s' ", class.governsID, class.name);
661
662         aggregate_list("AUX", list);
663
664         var may_list = undefined;
665         var must_list = undefined;
666
667         for (i=0;i<list.length;i++) {
668                 var c = list[i];
669                 var list2;
670                 list2 = attribute_list(objectclasses[c], 
671                                        "mayContain", "systemMayContain");
672                 may_list = list_append(may_list, list2);
673                 list2 = attribute_list(objectclasses[c], 
674                                        "mustContain", "systemMustContain");
675                 must_list = list_append(must_list, list2);
676         }
677
678         aggregate_list("MUST", must_list);
679         aggregate_list("MAY", may_list);
680
681         printf(")\n");
682 }
683
684 /*
685   write the aggregate record for an attribute
686 */
687 function write_aggregate_attribute(attrib) {
688         printf("attributeTypes: ( %s NAME '%s' SYNTAX '%s' ", 
689                attrib.attributeID, attrib.name, 
690                map_attribute_syntax(attrib.attributeSyntax));
691         if (attrib['isSingleValued'] == "TRUE") {
692                 printf("SINGLE-VALUE ");
693         }
694         if (attrib['systemOnly'] == "TRUE") {
695                 printf("NO-USER-MODIFICATION ");
696         }
697
698         printf(")\n");
699 }
700
701
702 /*
703   write the aggregate record
704 */
705 function write_aggregate() {
706         printf("dn: CN=Aggregate,${SCHEMADN}\n");
707         print("objectClass: top
708 objectClass: subSchema
709 objectCategory: CN=SubSchema,${SCHEMADN}
710 ");
711         if (dump_subschema_auto == undefined) {
712                 return; 
713         }
714
715         for (i in objectclasses) {
716                 write_aggregate_objectclass(objectclasses[i]);
717         }
718         for (i in attributes) {
719                 write_aggregate_attribute(attributes[i]);
720         }
721         for (i in objectclasses) {
722                 write_aggregate_ditcontentrule(objectclasses[i]);
723         }
724 }
725
726 /*
727   load a list from a file
728 */
729 function load_list(file) {
730         var sys = sys_init();
731         var s = sys.file_load(file);
732         var a = split("\n", s);
733         return a;
734 }
735
736 /* get the rootDSE */
737 var res = ldb.search("", "", ldb.SCOPE_BASE);
738 rootDse = res.msgs[0];
739
740 /* load the list of classes we are interested in */
741 var classes = load_list(classfile);
742 var i;
743 for (i=0;i<classes.length;i++) {
744         var classname = classes[i];
745         var class = build_objectclass(ldb, classname);
746         if (class != undefined) {
747                 objectclasses[classname] = class;
748         }
749 }
750
751
752 /*
753   expand the objectclass list as needed
754 */
755 var num_classes = 0;
756 var expanded = 0;
757 /* calculate the actual number of classes */
758 for (i in objectclasses) {
759         num_classes++;
760 }
761 /* so EJS do not have while nor the break statement
762    cannot find any other way than doing more loops
763    than necessary to recursively expand all classes
764  */
765 var inf;
766 for (inf = 0;inf < 500; inf++) {
767         if (expanded < num_classes) {
768                 for (i in objectclasses) {
769                         var n = objectclasses[i];
770                         if (objectclasses_expanded[i] != "DONE") {
771                                 expand_objectclass(ldb, objectclasses[i]);
772                                 objectclasses_expanded[i] = "DONE";
773                                 expanded++;
774                         }
775                 }
776                 /* recalculate the actual number of classes */
777                 num_classes = 0;
778                 for (i in objectclasses) {
779                         num_classes++;
780                 }
781         }
782 }
783
784 /*
785   find objectclass properties
786 */
787 for (i in objectclasses) {
788         find_objectclass_properties(ldb, objectclasses[i]);
789 }
790
791 /*
792   form the full list of attributes
793 */
794 for (i in objectclasses) {
795         add_objectclass_attributes(ldb, objectclasses[i]);
796 }
797
798 /* and attribute properties */
799 for (i in attributes) {
800         find_attribute_properties(ldb, attributes[i]);
801 }
802
803 /*
804   trim the 'may' attribute lists to those really needed
805 */
806 for (i in objectclasses) {
807         trim_objectclass_attributes(ldb, objectclasses[i]);
808 }
809
810 /*
811   dump an ldif form of the attributes and objectclasses
812 */
813 if (dump_attributes != undefined) {
814         write_ldif(attributes, attrib_attrs);
815 }
816 if (dump_classes != undefined) {
817         write_ldif(objectclasses, class_attrs);
818 }
819 if (dump_subschema != undefined) {
820         write_aggregate();
821 }
822
823 if (verbose == undefined) {
824         exit(0);
825 }
826
827 /*
828   dump list of objectclasses
829 */
830 printf("objectClasses:\n")
831 for (i in objectclasses) {
832         printf("\t%s\n", i);
833 }
834 printf("attributes:\n")
835 for (i in attributes) {
836         printf("\t%s\n", i);
837 }
838
839 printf("autocreated attributes:\n");
840 for (i in attributes) {
841         if (attributes[i].autocreate == true) {
842                 printf("\t%s\n", i);
843         }
844 }
845
846 return 0;