076d4343603cd2c03ba29cee2e43be657cf07810
[samba.git] / testprogs / ejs / samba3sam.js
1 #!/usr/bin/env smbscript
2 /*
3   (C) Jelmer Vernooij <jelmer@samba.org> 2005
4   (C) Martin Kuehl <mkhl@samba.org> 2006
5   Published under the GNU GPL
6   Sponsored by Google Summer of Code
7  */
8
9 var sys;
10 var options = GetOptions(ARGV, "POPT_AUTOHELP", "POPT_COMMON_SAMBA");
11 if (options == undefined) {
12         println("Failed to parse options");
13         return -1;
14 }
15
16 libinclude("base.js");
17
18 if (options.ARGV.length != 2) {
19         println("Usage: samba3sam.js <TESTDIR> <DATADIR>");
20         return -1;
21 }
22
23 var prefix = options.ARGV[0];
24 var datadir = options.ARGV[1];
25
26 function setup_data(obj, ldif)
27 {
28         assert(ldif != undefined);
29         ldif = substitute_var(ldif, obj);
30         assert(ldif != undefined);
31         var ok = obj.db.add(ldif);
32         assert(ok);
33 }
34
35 function setup_modules(ldb, s3, s4, ldif)
36 {
37         assert(ldif != undefined);
38         ldif = substitute_var(ldif, s4);
39         assert(ldif != undefined);
40         var ok = ldb.add(ldif);
41         assert(ok);
42
43         var ldif = "
44 dn: @MAP=samba3sam
45 @FROM: " + s4.BASEDN + "
46 @TO: sambaDomainName=TESTS," + s3.BASEDN + "
47
48 dn: @MODULES
49 @LIST: rootdse,paged_results,server_sort,extended_dn,asq,samldb,objectclass,password_hash,operational,objectguid,rdn_name,samba3sam,partition
50
51 dn: @PARTITION
52 partition: " + s4.BASEDN + ":" + s4.url + "
53 partition: " + s3.BASEDN + ":" + s3.url + "
54 replicateEntries: @SUBCLASSES
55 replicateEntries: @ATTRIBUTES
56 replicateEntries: @INDEXLIST
57 ";
58         var ok = ldb.add(ldif);
59         assert(ok);
60 }
61
62 function test_s3sam_search(ldb)
63 {
64         println("Looking up by non-mapped attribute");
65         var msg = ldb.search("(cn=Administrator)");
66         assert(msg.length == 1);
67         assert(msg[0].cn == "Administrator");
68
69         println("Looking up by mapped attribute");
70         var msg = ldb.search("(name=Backup Operators)");
71         assert(msg.length == 1);
72         assert(msg[0].name == "Backup Operators");
73
74         println("Looking up by old name of renamed attribute");
75         var msg = ldb.search("(displayName=Backup Operators)");
76         assert(msg.length == 0);
77
78         println("Looking up mapped entry containing SID");
79         var msg = ldb.search("(cn=Replicator)");
80         assert(msg.length == 1);
81         println(msg[0].dn);
82         assert(msg[0].dn == "cn=Replicator,ou=Groups,dc=vernstok,dc=nl");
83         assert(msg[0].objectSid == "S-1-5-21-4231626423-2410014848-2360679739-552");
84
85         println("Checking mapping of objectClass");
86         var oc = msg[0].objectClass;
87         assert(oc != undefined);
88         for (var i in oc) {
89                 assert(oc[i] == "posixGroup" || oc[i] == "group");
90         }
91
92         println("Looking up by objectClass");
93         var msg = ldb.search("(|(objectClass=user)(cn=Administrator))");
94         assert(msg != undefined);
95         assert(msg.length == 2);
96         for (var i = 0; i < msg.length; i++) {
97                 assert((msg[i].dn == "unixName=Administrator,ou=Users,dc=vernstok,dc=nl") ||
98                        (msg[i].dn == "unixName=nobody,ou=Users,dc=vernstok,dc=nl"));
99         }
100 }
101
102 function test_s3sam_modify(ldb, s3)
103 {
104         var msg, ok;
105         println("Adding a record that will be fallbacked");
106         ok = ldb.add("
107 dn: cn=Foo
108 foo: bar
109 blah: Blie
110 cn: Foo
111 showInAdvancedViewOnly: TRUE
112 ");
113         if (!ok) {
114                 println(ldb.errstring());
115                 assert(ok);
116         }
117         assert(ok);
118
119         println("Checking for existence of record (local)");
120         /* TODO: This record must be searched in the local database, which is currently only supported for base searches
121          * msg = ldb.search("(cn=Foo)", new Array('foo','blah','cn','showInAdvancedViewOnly'));
122          * TODO: Actually, this version should work as well but doesn't...
123          * 
124          */
125         var attrs =  new Array('foo','blah','cn','showInAdvancedViewOnly');
126         msg = ldb.search("(cn=Foo)", "cn=Foo", ldb.LDB_SCOPE_BASE, attrs);
127         assert(msg.length == 1);
128         assert(msg[0].showInAdvancedViewOnly == "TRUE");
129         assert(msg[0].foo == "bar");
130         assert(msg[0].blah == "Blie");
131
132         println("Adding record that will be mapped");
133         ok = ldb.add("
134 dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
135 objectClass: user
136 unixName: bin
137 unicodePwd: geheim
138 cn: Niemand
139 ");
140         if (!ok) {
141                 println(ldb.errstring());
142                 assert(ok);
143         }
144         assert(ok);
145
146         println("Checking for existence of record (remote)");
147         msg = ldb.search("(unixName=bin)", new Array('unixName','cn','dn', 'unicodePwd'));
148         assert(msg.length == 1);
149         assert(msg[0].cn == "Niemand"); 
150         assert(msg[0].unicodePwd == "geheim");
151
152         println("Checking for existence of record (local && remote)");
153         msg = ldb.search("(&(unixName=bin)(unicodePwd=geheim))", new Array('unixName','cn','dn', 'unicodePwd'));
154         assert(msg.length == 1);                // TODO: should check with more records
155         assert(msg[0].cn == "Niemand");
156         assert(msg[0].unixName == "bin");
157         assert(msg[0].unicodePwd == "geheim");
158
159         println("Checking for existence of record (local || remote)");
160         msg = ldb.search("(|(unixName=bin)(unicodePwd=geheim))", new Array('unixName','cn','dn', 'unicodePwd'));
161         println("got " + msg.length + " replies");
162         assert(msg.length == 1);                // TODO: should check with more records
163         assert(msg[0].cn == "Niemand");
164         assert(msg[0].unixName == "bin" || msg[0].unicodePwd == "geheim");
165
166         println("Checking for data in destination database");
167         msg = s3.db.search("(cn=Niemand)");
168         assert(msg.length >= 1);
169         assert(msg[0].sambaSID == "S-1-5-21-4231626423-2410014848-2360679739-2001");
170         assert(msg[0].displayName == "Niemand");
171
172         println("Adding attribute...");
173         ok = ldb.modify("
174 dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
175 changetype: modify
176 add: description
177 description: Blah
178 ");
179         if (!ok) {
180                 println(ldb.errstring());
181                 assert(ok);
182         }
183         assert(ok);
184
185         println("Checking whether changes are still there...");
186         msg = ldb.search("(cn=Niemand)");
187         assert(msg.length >= 1);
188         assert(msg[0].cn == "Niemand");
189         assert(msg[0].description == "Blah");
190
191         println("Modifying attribute...");
192         ok = ldb.modify("
193 dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
194 changetype: modify
195 replace: description
196 description: Blie
197 ");
198                 if (!ok) {
199                         println(ldb.errstring());
200                         assert(ok);
201                 }
202         assert(ok);
203
204         println("Checking whether changes are still there...");
205         msg = ldb.search("(cn=Niemand)");
206         assert(msg.length >= 1);
207         assert(msg[0].description == "Blie");
208
209         println("Deleting attribute...");
210         ok = ldb.modify("
211 dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
212 changetype: modify
213 delete: description
214 ");
215         if (!ok) {
216                 println(ldb.errstring());
217                 assert(ok);
218         }
219         assert(ok);
220
221         println("Checking whether changes are no longer there...");
222         msg = ldb.search("(cn=Niemand)");
223         assert(msg.length >= 1);
224         assert(msg[0].description == undefined);
225
226         println("Renaming record...");
227         ok = ldb.rename("cn=Niemand,cn=Users,dc=vernstok,dc=nl", "cn=Niemand2,cn=Users,dc=vernstok,dc=nl");
228         assert(ok);
229
230         println("Checking whether DN has changed...");
231         msg = ldb.search("(cn=Niemand2)");
232         assert(msg.length == 1);
233         assert(msg[0].dn == "cn=Niemand2,cn=Users,dc=vernstok,dc=nl");
234
235         println("Deleting record...");
236         ok = ldb.del("cn=Niemand2,cn=Users,dc=vernstok,dc=nl");
237         if (!ok) {
238                 println(ldb.errstring());
239                 assert(ok);
240         }
241
242         println("Checking whether record is gone...");
243         msg = ldb.search("(cn=Niemand2)");
244         assert(msg.length == 0);
245 }
246
247 function test_map_search(ldb, s3, s4)
248 {
249         println("Running search tests on mapped data");
250         var res;
251         var dn;
252         var attrs;
253
254         /* Add a set of split records */
255         var ldif = "
256 dn: " + s4.dn("cn=X") + "
257 objectClass: user
258 cn: X
259 codePage: x
260 revision: x
261 objectCategory: x
262 nextRid: y
263 lastLogon: x
264 description: x
265 objectSid: S-1-5-21-4231626423-2410014848-2360679739-552
266 primaryGroupID: 1-5-21-4231626423-2410014848-2360679739-512
267
268 dn: " + s4.dn("cn=Y") + "
269 objectClass: top
270 cn: Y
271 codePage: x
272 revision: x
273 objectCategory: y
274 nextRid: y
275 lastLogon: y
276 description: x
277
278 dn: " + s4.dn("cn=Z") + "
279 objectClass: top
280 cn: Z
281 codePage: x
282 revision: y
283 objectCategory: z
284 nextRid: y
285 lastLogon: z
286 description: y
287 ";
288         ldif = substitute_var(ldif, s4);
289         assert(ldif != undefined);
290         var ok = ldb.add(ldif);
291         assert(ok);
292
293         /* Add a set of remote records */
294         var ldif = "
295 dn: " + s3.dn("cn=A") + "
296 objectClass: posixAccount
297 cn: A
298 sambaNextRid: x
299 sambaBadPasswordCount: x
300 sambaLogonTime: x
301 description: x
302 sambaSID: S-1-5-21-4231626423-2410014848-2360679739-552
303 sambaPrimaryGroupSID: S-1-5-21-4231626423-2410014848-2360679739-512
304
305 dn: " + s3.dn("cn=B") + "
306 objectClass: top
307 cn:B
308 sambaNextRid: x
309 sambaBadPasswordCount: x
310 sambaLogonTime: y
311 description: x
312
313 dn: " + s3.dn("cn=C") + "
314 objectClass: top
315 cn: C
316 sambaNextRid: x
317 sambaBadPasswordCount: y
318 sambaLogonTime: z
319 description: y
320 ";
321         ldif = substitute_var(ldif, s3);
322         assert(ldif != undefined);
323         var ok = s3.db.add(ldif);
324         assert(ok);
325
326         println("Testing search by DN");
327
328         /* Search remote record by local DN */
329         dn = s4.dn("cn=A");
330         attrs = new Array("objectCategory", "lastLogon");
331         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
332         assert(res != undefined);
333         assert(res.length == 1);
334         assert(res[0].dn == dn);
335         assert(res[0].objectCategory == undefined);
336         assert(res[0].lastLogon == "x");
337
338         /* Search remote record by remote DN */
339         dn = s3.dn("cn=A");
340         attrs = new Array("objectCategory", "lastLogon", "sambaLogonTime");
341         res = s3.db.search("", dn, ldb.SCOPE_BASE, attrs);
342         assert(res != undefined);
343         assert(res.length == 1);
344         assert(res[0].dn == dn);
345         assert(res[0].objectCategory == undefined);
346         assert(res[0].lastLogon == undefined);
347         assert(res[0].sambaLogonTime == "x");
348
349         /* Search split record by local DN */
350         dn = s4.dn("cn=X");
351         attrs = new Array("objectCategory", "lastLogon");
352         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
353         assert(res != undefined);
354         assert(res.length == 1);
355         assert(res[0].dn == dn);
356         assert(res[0].objectCategory == "x");
357         assert(res[0].lastLogon == "x");
358
359         /* Search split record by remote DN */
360         dn = s3.dn("cn=X");
361         attrs = new Array("objectCategory", "lastLogon", "sambaLogonTime");
362         res = s3.db.search("", dn, ldb.SCOPE_BASE, attrs);
363         assert(res != undefined);
364         assert(res.length == 1);
365         assert(res[0].dn == dn);
366         assert(res[0].objectCategory == undefined);
367         assert(res[0].lastLogon == undefined);
368         assert(res[0].sambaLogonTime == "x");
369
370         println("Testing search by attribute");
371
372         /* Search by ignored attribute */
373         attrs = new Array("objectCategory", "lastLogon");
374         res = ldb.search("(revision=x)", NULL, ldb. SCOPE_DEFAULT, attrs);
375         assert(res != undefined);
376         assert(res.length == 2);
377         assert(res[0].dn == s4.dn("cn=Y"));
378         assert(res[0].objectCategory == "y");
379         assert(res[0].lastLogon == "y");
380         assert(res[1].dn == s4.dn("cn=X"));
381         assert(res[1].objectCategory == "x");
382         assert(res[1].lastLogon == "x");
383
384         /* Search by kept attribute */
385         attrs = new Array("objectCategory", "lastLogon");
386         res = ldb.search("(description=y)", NULL, ldb. SCOPE_DEFAULT, attrs);
387         assert(res != undefined);
388         assert(res.length == 2);
389         assert(res[0].dn == s4.dn("cn=Z"));
390         assert(res[0].objectCategory == "z");
391         assert(res[0].lastLogon == "z");
392         assert(res[1].dn == s4.dn("cn=C"));
393         assert(res[1].objectCategory == undefined);
394         assert(res[1].lastLogon == "z");
395
396         /* Search by renamed attribute */
397         attrs = new Array("objectCategory", "lastLogon");
398         res = ldb.search("(badPwdCount=x)", NULL, ldb. SCOPE_DEFAULT, attrs);
399         assert(res != undefined);
400         assert(res.length == 2);
401         assert(res[0].dn == s4.dn("cn=B"));
402         assert(res[0].objectCategory == undefined);
403         assert(res[0].lastLogon == "y");
404         assert(res[1].dn == s4.dn("cn=A"));
405         assert(res[1].objectCategory == undefined);
406         assert(res[1].lastLogon == "x");
407
408         /* Search by converted attribute */
409         attrs = new Array("objectCategory", "lastLogon", "objectSid");
410         /* TODO:
411            Using the SID directly in the parse tree leads to conversion
412            errors, letting the search fail with no results.
413         res = ldb.search("(objectSid=S-1-5-21-4231626423-2410014848-2360679739-552)", NULL, ldb. SCOPE_DEFAULT, attrs);
414         */
415         res = ldb.search("(objectSid=*)", NULL, ldb. SCOPE_DEFAULT, attrs);
416         assert(res != undefined);
417         assert(res.length == 2);
418         assert(res[0].dn == s4.dn("cn=X"));
419         assert(res[0].objectCategory == "x");
420         assert(res[0].lastLogon == "x");
421         assert(res[0].objectSid == "S-1-5-21-4231626423-2410014848-2360679739-552");
422         assert(res[1].dn == s4.dn("cn=A"));
423         assert(res[1].objectCategory == undefined);
424         assert(res[1].lastLogon == "x");
425         assert(res[1].objectSid == "S-1-5-21-4231626423-2410014848-2360679739-552");
426
427         /* Search by generated attribute */
428         /* In most cases, this even works when the mapping is missing
429          * a `convert_operator' by enumerating the remote db. */
430         attrs = new Array("objectCategory", "lastLogon", "primaryGroupID");
431         res = ldb.search("(primaryGroupID=512)", NULL, ldb. SCOPE_DEFAULT, attrs);
432         assert(res != undefined);
433         assert(res.length == 1);
434         assert(res[0].dn == s4.dn("cn=A"));
435         assert(res[0].objectCategory == undefined);
436         assert(res[0].lastLogon == "x");
437         assert(res[0].primaryGroupID == "512");
438
439         /* TODO: There should actually be two results, A and X.  The
440          * primaryGroupID of X seems to get corrupted somewhere, and the
441          * objectSid isn't available during the generation of remote (!) data,
442          * which can be observed with the following search.  Also note that Xs
443          * objectSid seems to be fine in the previous search for objectSid... */
444         /*
445         res = ldb.search("(primaryGroupID=*)", NULL, ldb. SCOPE_DEFAULT, attrs);
446         println(res.length + " results found");
447         for (i=0;i<res.length;i++) {
448                 for (obj in res[i]) {
449                         println(obj + ": " + res[i][obj]);
450                 }
451                 println("---");
452         }
453         */
454
455         /* Search by remote name of renamed attribute */
456         attrs = new Array("objectCategory", "lastLogon");
457         res = ldb.search("(sambaBadPasswordCount=*)", "", ldb. SCOPE_DEFAULT, attrs);
458         assert(res != undefined);
459         assert(res.length == 0);
460
461         /* Search by objectClass */
462         attrs = new Array("objectCategory", "lastLogon", "objectClass");
463         res = ldb.search("(objectClass=user)", NULL, ldb. SCOPE_DEFAULT, attrs);
464         assert(res != undefined);
465         assert(res.length == 2);
466         assert(res[0].dn == s4.dn("cn=X"));
467         assert(res[0].objectCategory == "x");
468         assert(res[0].lastLogon == "x");
469         assert(res[0].objectClass != undefined);
470         assert(res[0].objectClass[3] == "user");
471         assert(res[1].dn == s4.dn("cn=A"));
472         assert(res[1].objectCategory == undefined);
473         assert(res[1].lastLogon == "x");
474         assert(res[1].objectClass != undefined);
475         assert(res[1].objectClass[0] == "user");
476
477         /* Prove that the objectClass is actually used for the search */
478         res = ldb.search("(|(objectClass=user)(badPwdCount=x))", NULL, ldb. SCOPE_DEFAULT, attrs);
479         assert(res != undefined);
480         assert(res.length == 3);
481         assert(res[0].dn == s4.dn("cn=B"));
482         assert(res[0].objectCategory == undefined);
483         assert(res[0].lastLogon == "y");
484         assert(res[0].objectClass != undefined);
485         for (i=0;i<res[0].objectClass.length;i++) {
486                 assert(res[0].objectClass[i] != "user");
487         }
488         assert(res[1].dn == s4.dn("cn=X"));
489         assert(res[1].objectCategory == "x");
490         assert(res[1].lastLogon == "x");
491         assert(res[1].objectClass != undefined);
492         assert(res[1].objectClass[3] == "user");
493         assert(res[2].dn == s4.dn("cn=A"));
494         assert(res[2].objectCategory == undefined);
495         assert(res[2].lastLogon == "x");
496         assert(res[2].objectClass != undefined);
497         assert(res[2].objectClass[0] == "user");
498
499         println("Testing search by parse tree");
500
501         /* Search by conjunction of local attributes */
502         attrs = new Array("objectCategory", "lastLogon");
503         res = ldb.search("(&(codePage=x)(revision=x))", NULL, ldb.SCOPE_DEFAULT, attrs);
504         assert(res != undefined);
505         assert(res.length == 2);
506         assert(res[0].dn == s4.dn("cn=Y"));
507         assert(res[0].objectCategory == "y");
508         assert(res[0].lastLogon == "y");
509         assert(res[1].dn == s4.dn("cn=X"));
510         assert(res[1].objectCategory == "x");
511         assert(res[1].lastLogon == "x");
512
513         /* Search by conjunction of remote attributes */
514         attrs = new Array("objectCategory", "lastLogon");
515         res = ldb.search("(&(lastLogon=x)(description=x))", NULL, ldb.SCOPE_DEFAULT, attrs);
516         assert(res != undefined);
517         assert(res.length == 2);
518         assert(res[0].dn == s4.dn("cn=X"));
519         assert(res[0].objectCategory == "x");
520         assert(res[0].lastLogon == "x");
521         assert(res[1].dn == s4.dn("cn=A"));
522         assert(res[1].objectCategory == undefined);
523         assert(res[1].lastLogon == "x");
524         
525         /* Search by conjunction of local and remote attribute */
526         attrs = new Array("objectCategory", "lastLogon");
527         res = ldb.search("(&(codePage=x)(description=x))", NULL, ldb.SCOPE_DEFAULT, attrs);
528         assert(res != undefined);
529         assert(res.length == 2);
530         assert(res[0].dn == s4.dn("cn=Y"));
531         assert(res[0].objectCategory == "y");
532         assert(res[0].lastLogon == "y");
533         assert(res[1].dn == s4.dn("cn=X"));
534         assert(res[1].objectCategory == "x");
535         assert(res[1].lastLogon == "x");
536
537         /* Search by conjunction of local and remote attribute w/o match */
538         attrs = new Array("objectCategory", "lastLogon");
539         res = ldb.search("(&(codePage=x)(nextRid=x))", NULL, ldb.SCOPE_DEFAULT, attrs);
540         assert(res != undefined);
541         assert(res.length == 0);
542         res = ldb.search("(&(revision=x)(lastLogon=z))", NULL, ldb.SCOPE_DEFAULT, attrs);
543         assert(res != undefined);
544         assert(res.length == 0);
545
546         /* Search by disjunction of local attributes */
547         attrs = new Array("objectCategory", "lastLogon");
548         res = ldb.search("(|(revision=x)(objectCategory=x))", NULL, ldb.SCOPE_DEFAULT, attrs);
549         assert(res != undefined);
550         assert(res.length == 2);
551         assert(res[0].dn == s4.dn("cn=Y"));
552         assert(res[0].objectCategory == "y");
553         assert(res[0].lastLogon == "y");
554         assert(res[1].dn == s4.dn("cn=X"));
555         assert(res[1].objectCategory == "x");
556         assert(res[1].lastLogon == "x");
557
558         /* Search by disjunction of remote attributes */
559         attrs = new Array("objectCategory", "lastLogon");
560         res = ldb.search("(|(badPwdCount=x)(lastLogon=x))", NULL, ldb.SCOPE_DEFAULT, attrs);
561         assert(res != undefined);
562         assert(res.length == 3);
563         assert(res[0].dn == s4.dn("cn=B"));
564         assert(res[0].objectCategory == undefined);
565         assert(res[0].lastLogon == "y");
566         assert(res[1].dn == s4.dn("cn=X"));
567         assert(res[1].objectCategory == "x");
568         assert(res[1].lastLogon == "x");
569         assert(res[2].dn == s4.dn("cn=A"));
570         assert(res[2].objectCategory == undefined);
571         assert(res[2].lastLogon == "x");
572
573         /* Search by disjunction of local and remote attribute */
574         attrs = new Array("objectCategory", "lastLogon");
575         res = ldb.search("(|(revision=x)(lastLogon=y))", NULL, ldb.SCOPE_DEFAULT, attrs);
576         assert(res != undefined);
577         assert(res.length == 3);
578         assert(res[0].dn == s4.dn("cn=Y"));
579         assert(res[0].objectCategory == "y");
580         assert(res[0].lastLogon == "y");
581         assert(res[1].dn == s4.dn("cn=B"));
582         assert(res[1].objectCategory == undefined);
583         assert(res[1].lastLogon == "y");
584         assert(res[2].dn == s4.dn("cn=X"));
585         assert(res[2].objectCategory == "x");
586         assert(res[2].lastLogon == "x");
587
588         /* Search by disjunction of local and remote attribute w/o match */
589         attrs = new Array("objectCategory", "lastLogon");
590         res = ldb.search("(|(codePage=y)(nextRid=z))", NULL, ldb.SCOPE_DEFAULT, attrs);
591         assert(res != undefined);
592         assert(res.length == 0);
593
594         /* Search by negated local attribute */
595         attrs = new Array("objectCategory", "lastLogon");
596         res = ldb.search("(!(revision=x))", NULL, ldb.SCOPE_DEFAULT, attrs);
597         assert(res != undefined);
598         assert(res.length == 4);
599         assert(res[0].dn == s4.dn("cn=B"));
600         assert(res[0].objectCategory == undefined);
601         assert(res[0].lastLogon == "y");
602         assert(res[1].dn == s4.dn("cn=A"));
603         assert(res[1].objectCategory == undefined);
604         assert(res[1].lastLogon == "x");
605         assert(res[2].dn == s4.dn("cn=Z"));
606         assert(res[2].objectCategory == "z");
607         assert(res[2].lastLogon == "z");
608         assert(res[3].dn == s4.dn("cn=C"));
609         assert(res[3].objectCategory == undefined);
610         assert(res[3].lastLogon == "z");
611
612         /* Search by negated remote attribute */
613         attrs = new Array("objectCategory", "lastLogon");
614         res = ldb.search("(!(description=x))", NULL, ldb.SCOPE_DEFAULT, attrs);
615         assert(res != undefined);
616         assert(res.length == 2);
617         assert(res[0].dn == s4.dn("cn=Z"));
618         assert(res[0].objectCategory == "z");
619         assert(res[0].lastLogon == "z");
620         assert(res[1].dn == s4.dn("cn=C"));
621         assert(res[1].objectCategory == undefined);
622         assert(res[1].lastLogon == "z");
623
624         /* Search by negated conjunction of local attributes */
625         attrs = new Array("objectCategory", "lastLogon");
626         res = ldb.search("(!(&(codePage=x)(revision=x)))", NULL, ldb.SCOPE_DEFAULT, attrs);
627         assert(res != undefined);
628         assert(res.length == 4);
629         assert(res[0].dn == s4.dn("cn=B"));
630         assert(res[0].objectCategory == undefined);
631         assert(res[0].lastLogon == "y");
632         assert(res[1].dn == s4.dn("cn=A"));
633         assert(res[1].objectCategory == undefined);
634         assert(res[1].lastLogon == "x");
635         assert(res[2].dn == s4.dn("cn=Z"));
636         assert(res[2].objectCategory == "z");
637         assert(res[2].lastLogon == "z");
638         assert(res[3].dn == s4.dn("cn=C"));
639         assert(res[3].objectCategory == undefined);
640         assert(res[3].lastLogon == "z");
641
642         /* Search by negated conjunction of remote attributes */
643         attrs = new Array("objectCategory", "lastLogon");
644         res = ldb.search("(!(&(lastLogon=x)(description=x)))", NULL, ldb.SCOPE_DEFAULT, attrs);
645         assert(res != undefined);
646         assert(res.length == 4);
647         assert(res[0].dn == s4.dn("cn=Y"));
648         assert(res[0].objectCategory == "y");
649         assert(res[0].lastLogon == "y");
650         assert(res[1].dn == s4.dn("cn=B"));
651         assert(res[1].objectCategory == undefined);
652         assert(res[1].lastLogon == "y");
653         assert(res[2].dn == s4.dn("cn=Z"));
654         assert(res[2].objectCategory == "z");
655         assert(res[2].lastLogon == "z");
656         assert(res[3].dn == s4.dn("cn=C"));
657         assert(res[3].objectCategory == undefined);
658         assert(res[3].lastLogon == "z");
659
660         /* Search by negated conjunction of local and remote attribute */
661         attrs = new Array("objectCategory", "lastLogon");
662         res = ldb.search("(!(&(codePage=x)(description=x)))", NULL, ldb.SCOPE_DEFAULT, attrs);
663         assert(res != undefined);
664         assert(res.length == 4);
665         assert(res[0].dn == s4.dn("cn=B"));
666         assert(res[0].objectCategory == undefined);
667         assert(res[0].lastLogon == "y");
668         assert(res[1].dn == s4.dn("cn=A"));
669         assert(res[1].objectCategory == undefined);
670         assert(res[1].lastLogon == "x");
671         assert(res[2].dn == s4.dn("cn=Z"));
672         assert(res[2].objectCategory == "z");
673         assert(res[2].lastLogon == "z");
674         assert(res[3].dn == s4.dn("cn=C"));
675         assert(res[3].objectCategory == undefined);
676         assert(res[3].lastLogon == "z");
677
678         /* Search by negated disjunction of local attributes */
679         attrs = new Array("objectCategory", "lastLogon");
680         res = ldb.search("(!(|(revision=x)(objectCategory=x)))", NULL, ldb.SCOPE_DEFAULT, attrs);
681         assert(res != undefined);
682         assert(res[0].dn == s4.dn("cn=B"));
683         assert(res[0].objectCategory == undefined);
684         assert(res[0].lastLogon == "y");
685         assert(res[1].dn == s4.dn("cn=A"));
686         assert(res[1].objectCategory == undefined);
687         assert(res[1].lastLogon == "x");
688         assert(res[2].dn == s4.dn("cn=Z"));
689         assert(res[2].objectCategory == "z");
690         assert(res[2].lastLogon == "z");
691         assert(res[3].dn == s4.dn("cn=C"));
692         assert(res[3].objectCategory == undefined);
693         assert(res[3].lastLogon == "z");
694
695         /* Search by negated disjunction of remote attributes */
696         attrs = new Array("objectCategory", "lastLogon");
697         res = ldb.search("(!(|(badPwdCount=x)(lastLogon=x)))", NULL, ldb.SCOPE_DEFAULT, attrs);
698         assert(res != undefined);
699         assert(res.length == 3);
700         assert(res[0].dn == s4.dn("cn=Y"));
701         assert(res[0].objectCategory == "y");
702         assert(res[0].lastLogon == "y");
703         assert(res[1].dn == s4.dn("cn=Z"));
704         assert(res[1].objectCategory == "z");
705         assert(res[1].lastLogon == "z");
706         assert(res[2].dn == s4.dn("cn=C"));
707         assert(res[2].objectCategory == undefined);
708         assert(res[2].lastLogon == "z");
709
710         /* Search by negated disjunction of local and remote attribute */
711         attrs = new Array("objectCategory", "lastLogon");
712         res = ldb.search("(!(|(revision=x)(lastLogon=y)))", NULL, ldb.SCOPE_DEFAULT, attrs);
713         assert(res != undefined);
714         assert(res.length == 3);
715         assert(res[0].dn == s4.dn("cn=A"));
716         assert(res[0].objectCategory == undefined);
717         assert(res[0].lastLogon == "x");
718         assert(res[1].dn == s4.dn("cn=Z"));
719         assert(res[1].objectCategory == "z");
720         assert(res[1].lastLogon == "z");
721         assert(res[2].dn == s4.dn("cn=C"));
722         assert(res[2].objectCategory == undefined);
723         assert(res[2].lastLogon == "z");
724
725         /* Search by complex parse tree */
726         attrs = new Array("objectCategory", "lastLogon");
727         res = ldb.search("(|(&(revision=x)(objectCategory=x))(!(&(description=x)(nextRid=y)))(badPwdCount=y))", NULL, ldb.SCOPE_DEFAULT, attrs);
728         assert(res != undefined);
729         assert(res.length == 5);
730         assert(res[0].dn == s4.dn("cn=B"));
731         assert(res[0].objectCategory == undefined);
732         assert(res[0].lastLogon == "y");
733         assert(res[1].dn == s4.dn("cn=X"));
734         assert(res[1].objectCategory == "x");
735         assert(res[1].lastLogon == "x");
736         assert(res[2].dn == s4.dn("cn=A"));
737         assert(res[2].objectCategory == undefined);
738         assert(res[2].lastLogon == "x");
739         assert(res[3].dn == s4.dn("cn=Z"));
740         assert(res[3].objectCategory == "z");
741         assert(res[3].lastLogon == "z");
742         assert(res[4].dn == s4.dn("cn=C"));
743         assert(res[4].objectCategory == undefined);
744         assert(res[4].lastLogon == "z");
745
746         /* Clean up */
747         var dns = new Array();
748         dns[0] = s4.dn("cn=A");
749         dns[1] = s4.dn("cn=B");
750         dns[2] = s4.dn("cn=C");
751         dns[3] = s4.dn("cn=X");
752         dns[4] = s4.dn("cn=Y");
753         dns[5] = s4.dn("cn=Z");
754         for (i=0;i<dns.length;i++) {
755                 var ok = ldb.del(dns[i]);
756                 assert(ok);
757         }
758 }
759
760 function test_map_modify(ldb, s3, s4)
761 {
762         println("Running modification tests on mapped data");
763
764         var ldif;
765         var attrs;
766         var dn, dn2;
767         var res;
768         var ok;
769
770         println("Testing modification of local records");
771
772         /* Add local record */
773         dn = "cn=test,dc=idealx,dc=org";
774         ldif = "
775 dn: " + dn + "
776 cn: test
777 foo: bar
778 revision: 1
779 description: test
780 ";
781         ok = ldb.add(ldif);
782         assert(ok);
783         /* Check it's there */
784         attrs = new Array("foo", "revision", "description");
785         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
786         assert(res != undefined);
787         assert(res.length == 1);
788         assert(res[0].dn == dn);
789         assert(res[0].foo == "bar");
790         assert(res[0].revision == "1");
791         assert(res[0].description == "test");
792         /* Check it's not in the local db */
793         res = s4.db.search("(cn=test)", NULL, ldb.SCOPE_DEFAULT, attrs);
794         assert(res != undefined);
795         assert(res.length == 0);
796         /* Check it's not in the remote db */
797         res = s3.db.search("(cn=test)", NULL, ldb.SCOPE_DEFAULT, attrs);
798         assert(res != undefined);
799         assert(res.length == 0);
800
801         /* Modify local record */
802         ldif = "
803 dn: " + dn + "
804 replace: foo
805 foo: baz
806 replace: description
807 description: foo
808 ";
809         ok = ldb.modify(ldif);
810         assert(ok);
811         /* Check in local db */
812         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
813         assert(res != undefined);
814         assert(res.length == 1);
815         assert(res[0].dn == dn);
816         assert(res[0].foo == "baz");
817         assert(res[0].revision == "1");
818         assert(res[0].description == "foo");
819
820         /* Rename local record */
821         dn2 = "cn=toast,dc=idealx,dc=org";
822         ok = ldb.rename(dn, dn2);
823         assert(ok);
824         /* Check in local db */
825         res = ldb.search("", dn2, ldb.SCOPE_BASE, attrs);
826         assert(res != undefined);
827         assert(res.length == 1);
828         assert(res[0].dn == dn2);
829         assert(res[0].foo == "baz");
830         assert(res[0].revision == "1");
831         assert(res[0].description == "foo");
832
833         /* Delete local record */
834         ok = ldb.del(dn2);
835         assert(ok);
836         /* Check it's gone */
837         res = ldb.search("", dn2, ldb.SCOPE_BASE);
838         assert(res != undefined);
839         assert(res.length == 0);
840
841         println("Testing modification of remote records");
842
843         /* Add remote record */
844         dn = s4.dn("cn=test");
845         dn2 = s3.dn("cn=test");
846         ldif = "
847 dn: " + dn2 + "
848 cn: test
849 description: foo
850 sambaBadPasswordCount: 3
851 sambaNextRid: 1001
852 ";
853         ok = s3.db.add(ldif);
854         assert(ok);
855         /* Check it's there */
856         attrs = new Array("description", "sambaBadPasswordCount", "sambaNextRid");
857         res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs);
858         assert(res != undefined);
859         assert(res.length == 1);
860         assert(res[0].dn == dn2);
861         assert(res[0].description == "foo");
862         assert(res[0].sambaBadPasswordCount == "3");
863         assert(res[0].sambaNextRid == "1001");
864         /* Check in mapped db */
865         attrs = new Array("description", "badPwdCount", "nextRid");
866         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
867         assert(res != undefined);
868         assert(res.length == 1);
869         assert(res[0].dn == dn);
870         assert(res[0].description == "foo");
871         assert(res[0].badPwdCount == "3");
872         assert(res[0].nextRid == "1001");
873         /* Check in local db */
874         res = s4.db.search("", dn, ldb.SCOPE_BASE, attrs);
875         assert(res != undefined);
876         assert(res.length == 0);
877
878         /* Modify remote data of remote record */
879         ldif = "
880 dn: " + dn + "
881 replace: description
882 description: test
883 replace: badPwdCount
884 badPwdCount: 4
885 ";
886         ok = ldb.modify(ldif);
887         /* Check in mapped db */
888         attrs = new Array("description", "badPwdCount", "nextRid");
889         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
890         assert(res != undefined);
891         assert(res.length == 1);
892         assert(res[0].dn == dn);
893         assert(res[0].description == "test");
894         assert(res[0].badPwdCount == "4");
895         assert(res[0].nextRid == "1001");
896         /* Check in remote db */
897         attrs = new Array("description", "sambaBadPasswordCount", "sambaNextRid");
898         res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs);
899         assert(res != undefined);
900         assert(res.length == 1);
901         assert(res[0].dn == dn2);
902         assert(res[0].description == "test");
903         assert(res[0].sambaBadPasswordCount == "4");
904         assert(res[0].sambaNextRid == "1001");
905
906         /* Rename remote record */
907         dn2 = s4.dn("cn=toast");
908         ok = ldb.rename(dn, dn2);
909         assert(ok);
910         /* Check in mapped db */
911         dn = dn2;
912         attrs = new Array("description", "badPwdCount", "nextRid");
913         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
914         assert(res != undefined);
915         assert(res.length == 1);
916         assert(res[0].dn == dn);
917         assert(res[0].description == "test");
918         assert(res[0].badPwdCount == "4");
919         assert(res[0].nextRid == "1001");
920         /* Check in remote db */
921         dn2 = s3.dn("cn=toast");
922         attrs = new Array("description", "sambaBadPasswordCount", "sambaNextRid");
923         res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs);
924         assert(res != undefined);
925         assert(res.length == 1);
926         assert(res[0].dn == dn2);
927         assert(res[0].description == "test");
928         assert(res[0].sambaBadPasswordCount == "4");
929         assert(res[0].sambaNextRid == "1001");
930
931         /* Delete remote record */
932         ok = ldb.del(dn);
933         assert(ok);
934         /* Check in mapped db */
935         res = ldb.search("", dn, ldb.SCOPE_BASE);
936         assert(res != undefined);
937         assert(res.length == 0);
938         /* Check in remote db */
939         res = s3.db.search("", dn2, ldb.SCOPE_BASE);
940         assert(res != undefined);
941         assert(res.length == 0);
942
943         /* Add remote record (same as before) */
944         dn = s4.dn("cn=test");
945         dn2 = s3.dn("cn=test");
946         ldif = "
947 dn: " + dn2 + "
948 cn: test
949 description: foo
950 sambaBadPasswordCount: 3
951 sambaNextRid: 1001
952 ";
953         ok = s3.db.add(ldif);
954         assert(ok);
955
956         /* Modify local data of remote record */
957         ldif = "
958 dn: " + dn + "
959 add: revision
960 revision: 1
961 replace: description
962 description: test
963 ";
964         ok = ldb.modify(ldif);
965         /* Check in mapped db */
966         attrs = new Array("revision", "description");
967         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
968         assert(res != undefined);
969         assert(res.length == 1);
970         assert(res[0].dn == dn);
971         assert(res[0].description == "test");
972         assert(res[0].revision == "1");
973         /* Check in remote db */
974         res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs);
975         assert(res != undefined);
976         assert(res.length == 1);
977         assert(res[0].dn == dn2);
978         assert(res[0].description == "test");
979         assert(res[0].revision == undefined);
980         /* Check in local db */
981         res = s4.db.search("", dn, ldb.SCOPE_BASE, attrs);
982         assert(res != undefined);
983         assert(res.length == 1);
984         assert(res[0].dn == dn);
985         assert(res[0].description == undefined);
986         assert(res[0].revision == "1");
987
988         /* Delete (newly) split record */
989         ok = ldb.del(dn);
990         assert(ok);
991
992         println("Testing modification of split records");
993
994         /* Add split record */
995         dn = s4.dn("cn=test");
996         dn2 = s3.dn("cn=test");
997         ldif = "
998 dn: " + dn + "
999 cn: test
1000 description: foo
1001 badPwdCount: 3
1002 nextRid: 1001
1003 revision: 1
1004 ";
1005         ok = ldb.add(ldif);
1006         assert(ok);
1007         /* Check it's there */
1008         attrs = new Array("description", "badPwdCount", "nextRid", "revision");
1009         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
1010         assert(res != undefined);
1011         assert(res.length == 1);
1012         assert(res[0].dn == dn);
1013         assert(res[0].description == "foo");
1014         assert(res[0].badPwdCount == "3");
1015         assert(res[0].nextRid == "1001");
1016         assert(res[0].revision == "1");
1017         /* Check in local db */
1018         res = s4.db.search("", dn, ldb.SCOPE_BASE, attrs);
1019         assert(res != undefined);
1020         assert(res.length == 1);
1021         assert(res[0].dn == dn);
1022         assert(res[0].description == undefined);
1023         assert(res[0].badPwdCount == undefined);
1024         assert(res[0].nextRid == undefined);
1025         assert(res[0].revision == "1");
1026         /* Check in remote db */
1027         attrs = new Array("description", "sambaBadPasswordCount", "sambaNextRid", "revision");
1028         res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs);
1029         assert(res != undefined);
1030         assert(res.length == 1);
1031         assert(res[0].dn == dn2);
1032         assert(res[0].description == "foo");
1033         assert(res[0].sambaBadPasswordCount == "3");
1034         assert(res[0].sambaNextRid == "1001");
1035         assert(res[0].revision == undefined);
1036
1037         /* Modify of split record */
1038         ldif = "
1039 dn: " + dn + "
1040 replace: description
1041 description: test
1042 replace: badPwdCount
1043 badPwdCount: 4
1044 replace: revision
1045 revision: 2
1046 ";
1047         ok = ldb.modify(ldif);
1048         assert(ok);
1049         /* Check in mapped db */
1050         attrs = new Array("description", "badPwdCount", "nextRid", "revision");
1051         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
1052         assert(res != undefined);
1053         assert(res.length == 1);
1054         assert(res[0].dn == dn);
1055         assert(res[0].description == "test");
1056         assert(res[0].badPwdCount == "4");
1057         assert(res[0].nextRid == "1001");
1058         assert(res[0].revision == "2");
1059         /* Check in local db */
1060         res = s4.db.search("", dn, ldb.SCOPE_BASE, attrs);
1061         assert(res != undefined);
1062         assert(res.length == 1);
1063         assert(res[0].dn == dn);
1064         assert(res[0].description == undefined);
1065         assert(res[0].badPwdCount == undefined);
1066         assert(res[0].nextRid == undefined);
1067         assert(res[0].revision == "2");
1068         /* Check in remote db */
1069         attrs = new Array("description", "sambaBadPasswordCount", "sambaNextRid", "revision");
1070         res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs);
1071         assert(res != undefined);
1072         assert(res.length == 1);
1073         assert(res[0].dn == dn2);
1074         assert(res[0].description == "test");
1075         assert(res[0].sambaBadPasswordCount == "4");
1076         assert(res[0].sambaNextRid == "1001");
1077         assert(res[0].revision == undefined);
1078
1079         /* Rename split record */
1080         dn2 = s4.dn("cn=toast");
1081         ok = ldb.rename(dn, dn2);
1082         assert(ok);
1083         /* Check in mapped db */
1084         dn = dn2;
1085         attrs = new Array("description", "badPwdCount", "nextRid", "revision");
1086         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
1087         assert(res != undefined);
1088         assert(res.length == 1);
1089         assert(res[0].dn == dn);
1090         assert(res[0].description == "test");
1091         assert(res[0].badPwdCount == "4");
1092         assert(res[0].nextRid == "1001");
1093         assert(res[0].revision == "2");
1094         /* Check in local db */
1095         res = s4.db.search("", dn, ldb.SCOPE_BASE, attrs);
1096         assert(res != undefined);
1097         assert(res.length == 1);
1098         assert(res[0].dn == dn);
1099         assert(res[0].description == undefined);
1100         assert(res[0].badPwdCount == undefined);
1101         assert(res[0].nextRid == undefined);
1102         assert(res[0].revision == "2");
1103         /* Check in remote db */
1104         dn2 = s3.dn("cn=toast");
1105         attrs = new Array("description", "sambaBadPasswordCount", "sambaNextRid", "revision");
1106         res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs);
1107         assert(res != undefined);
1108         assert(res.length == 1);
1109         assert(res[0].dn == dn2);
1110         assert(res[0].description == "test");
1111         assert(res[0].sambaBadPasswordCount == "4");
1112         assert(res[0].sambaNextRid == "1001");
1113         assert(res[0].revision == undefined);
1114
1115         /* Delete split record */
1116         ok = ldb.del(dn);
1117         assert(ok);
1118         /* Check in mapped db */
1119         res = ldb.search("", dn, ldb.SCOPE_BASE);
1120         assert(res != undefined);
1121         assert(res.length == 0);
1122         /* Check in local db */
1123         res = s4.db.search("", dn, ldb.SCOPE_BASE);
1124         assert(res != undefined);
1125         assert(res.length == 0);
1126         /* Check in remote db */
1127         res = s3.db.search("", dn2, ldb.SCOPE_BASE);
1128         assert(res != undefined);
1129         assert(res.length == 0);
1130 }
1131
1132 function make_dn(rdn)
1133 {
1134         return rdn + ",sambaDomainName=TESTS," + this.BASEDN;
1135 }
1136
1137 function make_s4dn(rdn)
1138 {
1139         return rdn + "," + this.BASEDN;
1140 }
1141
1142 var ldb = ldb_init();
1143
1144 sys = sys_init();
1145 var ldbfile = prefix + "/" + "test.ldb";
1146 var ldburl = "tdb://" + ldbfile;
1147
1148 var samba4 = new Object("samba4 partition info");
1149 samba4.file = prefix + "/" + "samba4.ldb";
1150 samba4.url = "tdb://" + samba4.file;
1151 samba4.BASEDN = "dc=vernstok,dc=nl";
1152 samba4.db = ldb_init();
1153 samba4.dn = make_s4dn;
1154
1155 var samba3 = new Object("samba3 partition info");
1156 samba3.file = prefix + "/" + "samba3.ldb";
1157 samba3.url = "tdb://" + samba3.file;
1158 samba3.BASEDN = "cn=Samba3Sam";
1159 samba3.db = ldb_init();
1160 samba3.dn = make_dn;
1161
1162 sys.unlink(ldbfile);
1163 sys.unlink(samba3.file);
1164 sys.unlink(samba4.file);
1165
1166 var ok = ldb.connect(ldburl);
1167 assert(ok);
1168 var ok = samba3.db.connect(samba3.url);
1169 assert(ok);
1170 var ok = samba4.db.connect(samba4.url);
1171 assert(ok);
1172
1173 setup_data(samba3, sys.file_load(datadir + "/" + "samba3.ldif"));
1174 setup_modules(ldb, samba3, samba4, sys.file_load(datadir + "/" + "provision_samba3sam.ldif"));
1175
1176 ldb = ldb_init();
1177 var ok = ldb.connect(ldburl);
1178 assert(ok);
1179
1180 test_s3sam_search(ldb);
1181 test_s3sam_modify(ldb, samba3);
1182
1183 sys.unlink(ldbfile);
1184 sys.unlink(samba3.file);
1185 sys.unlink(samba4.file);
1186
1187 ldb = ldb_init();
1188 var ok = ldb.connect(ldburl);
1189 assert(ok);
1190 samba3.db = ldb_init();
1191 var ok = samba3.db.connect(samba3.url);
1192 assert(ok);
1193 samba4.db = ldb_init();
1194 var ok = samba4.db.connect(samba4.url);
1195 assert(ok);
1196
1197 setup_modules(ldb, samba3, samba4, sys.file_load(datadir + "provision_samba3sam.ldif"));
1198
1199 ldb = ldb_init();
1200 var ok = ldb.connect(ldburl);
1201 assert(ok);
1202
1203 test_map_search(ldb, samba3, samba4);
1204 test_map_modify(ldb, samba3, samba4);
1205
1206 sys.unlink(ldbfile);
1207 sys.unlink(samba3.file);
1208 sys.unlink(samba4.file);
1209
1210 return 0;