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